diff options
author | Allen Akin <akin@users.sourceforge.net> | 1999-12-15 01:24:19 +0000 |
---|---|---|
committer | Allen Akin <akin@users.sourceforge.net> | 1999-12-15 01:24:19 +0000 |
commit | 402349fd7e464b18882e531f05668125a4a9d73b (patch) | |
tree | 0022594e4e0dd82f71b9dcd44c02fe1860f222b7 /src |
Initial revision
Diffstat (limited to 'src')
81 files changed, 11441 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..ee983b8 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,5 @@ +include $(GLEAN_ROOT)/make/common.mak + +DIRS=glh libs glean tools + +include $(GLEAN_ROOT)/make/null.mak diff --git a/src/glean/Makefile b/src/glean/Makefile new file mode 100644 index 0000000..09a2149 --- /dev/null +++ b/src/glean/Makefile @@ -0,0 +1,8 @@ +# Makefile for glean + +include $(GLEAN_ROOT)/make/common.mak + +TARGET=glean +LIB=-lstats -ldsurf -llex -limage -ltimer -ltiff -lGLU -lGL -lXmu -lXext -lX11 + +include $(GLEAN_ROOT)/make/app.mak diff --git a/src/glean/dsurf.cpp b/src/glean/dsurf.cpp new file mode 100644 index 0000000..4f6126d --- /dev/null +++ b/src/glean/dsurf.cpp @@ -0,0 +1,205 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// dsurf.cpp: implementation of drawing surface utilities + +#include <iostream> +#include <algorithm> +#include "dsurf.h" +#include "dsconfig.h" +#include "winsys.h" + +namespace { + +#if defined(__X11__) + +Colormap +ChooseColormap(Display* dpy, XVisualInfo* vi) { + // We could be polite here and search for a standard colormap, + // but the normal mode of operation should be that glean is + // running alone, so there doesn't seem to be much point in sharing. + + return XCreateColormap(dpy, RootWindow(dpy, vi->screen), + vi->visual, AllocNone); +} // ChooseColormap + +#endif + +} // anonymous namespace + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors +/////////////////////////////////////////////////////////////////////////////// +DrawingSurface::DrawingSurface(WindowSystem& ws, DrawingSurfaceConfig& c) { + // Link back to enclosing window system, so as to simplify bookkeeping: + winSys = &ws; + ws.surfaces.push_back(this); + + // Save pointer to configuration information: + config = &c; +} // DrawingSurface::DrawingSurface + +Window::Window(WindowSystem& ws, DrawingSurfaceConfig& c, int w, int h): + DrawingSurface(ws, c) { + +#if defined(__X11__) + +#if defined(GLX_VERSION_1_3) + +#error "XXX Need GLX 1.3 window-creation code" + +#else + + // XXX There's basically no error-handling code here. + // See XErrorHandler(). + + // Create the window: + XSetWindowAttributes xswa; + xswa.background_pixmap = None; + xswa.border_pixel = 0; + xswa.colormap = ChooseColormap(winSys->dpy, config->vi); + xswa.event_mask = StructureNotifyMask; + xWindow = XCreateWindow(winSys->dpy, + RootWindow(winSys->dpy, config->vi->screen), + 0, 0, w, h, + 0, + config->vi->depth, + InputOutput, + config->vi->visual, + CWBackPixmap|CWBorderPixel|CWColormap|CWEventMask, + &xswa); + + // Set attributes for the benefit of the window manager: + XSizeHints sizeHints; + sizeHints.width = w; + sizeHints.height = h; + sizeHints.x = 10; + sizeHints.y = 10; + sizeHints.flags = USSize | USPosition; + XSetStandardProperties(winSys->dpy, xWindow, "glean", "glean", + None, 0, 0, &sizeHints); + + // Map the window and wait for it to appear: + XMapWindow(winSys->dpy, xWindow); + XEvent event; + for (;;) { + XNextEvent(winSys->dpy, &event); + if (event.type == MapNotify && event.xmap.window == xWindow) + break; + } + +#endif + +#elif defined(__WIN__) + // XXX There's basically no error-handling code here. + // create the window + RECT r; + int style = WS_POPUP | WS_CAPTION | WS_BORDER; + + r.left = 50; + r.top = 50; + r.right = r.left + w; + r.bottom = r.top + h; + AdjustWindowRect(&r,style,FALSE); + + hWindow = CreateWindow("glean","glean", + style | WS_VISIBLE, + r.left,r.top,r.right - r.left,r.bottom - r.top, + NULL, NULL, + GetModuleHandle(NULL), + NULL); + + if (!hWindow) + return; + + hDC = GetDC(hWindow); + + PIXELFORMATDESCRIPTOR pfd; + SetPixelFormat(hDC,config->pfdID,&pfd); +#endif +} // Window::Window + +/////////////////////////////////////////////////////////////////////////////// +// Destructors +/////////////////////////////////////////////////////////////////////////////// + +void +DrawingSurface::commonDestructorCode() { + remove(winSys->surfaces.begin(), winSys->surfaces.end(), this); +} // DrawingSurface::commonDestructorCode + +Window::~Window() { + +#if defined(__X11__) + XDestroyWindow(winSys->dpy, xWindow); +#elif defined(__WIN__) + ReleaseDC(hWindow,hDC); + DestroyWindow(hWindow); +#endif + +} // Window::~Window + +/////////////////////////////////////////////////////////////////////////////// +// Utilities +/////////////////////////////////////////////////////////////////////////////// +void +Window::swap() { +# if defined(__X11__) + glXSwapBuffers(winSys->dpy, xWindow); +# elif defined(__WIN__) + SwapBuffers(hDC); +# endif +} // Window::swap + +#if defined(__WIN__) + +/////////////////////////////////////////////////////////////////////////////// +// Window procedure +/////////////////////////////////////////////////////////////////////////////// + +LRESULT CALLBACK +Window::WindowProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) +{ + switch (message) + { + default : + return DefWindowProc(hwnd, message, wParam, lParam); + + } + + return FALSE; +} + +#endif + +} // namespace GLEAN diff --git a/src/glean/dsurf.h b/src/glean/dsurf.h new file mode 100644 index 0000000..925127d --- /dev/null +++ b/src/glean/dsurf.h @@ -0,0 +1,83 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// dsurf.h: utilities for manipulating drawing surfaces + +#ifndef __dsurf_h__ +#define __dsurf_h__ + +#include "glwrap.h" + +namespace GLEAN { + +class WindowSystem; // Forward and mutually-recursive references +class DrawingSurfaceConfig; + +class DrawingSurface { + public: + DrawingSurface(WindowSystem& ws, DrawingSurfaceConfig& c); + virtual ~DrawingSurface() { } + + WindowSystem* winSys; // Window system that owns this surface. + DrawingSurfaceConfig* config; // Configuration of this surface. + + protected: + void commonDestructorCode(); + +}; // class DrawingSurface + +class Window: public DrawingSurface { + public: + Window(WindowSystem& ws, DrawingSurfaceConfig& c, int w, int h); + ~Window(); + + // Utilities: + + void swap(); + + // XXX Add constructors for more specialized window creation -- + // for example, at a particular screen location, with a particular + // parent window, etc. -- as needed by new tests. + +# if defined(__X11__) + ::Window xWindow; +# elif defined(__WIN__) + ::HWND hWindow; + ::HDC hDC; + + ::HDC get_dc() const {return hDC;} + static LRESULT CALLBACK WindowProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam); +# endif +}; // class Window + +} // namespace GLEAN + +#endif // __dsurf_h__ diff --git a/src/glean/environ.cpp b/src/glean/environ.cpp new file mode 100644 index 0000000..1856c58 --- /dev/null +++ b/src/glean/environ.cpp @@ -0,0 +1,150 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// environ.cpp: implementation of test environment class + +#include "environ.h" + +#if defined(__UNIX__) +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#elif defined(__MS__) + +#include <sys/stat.h> + +#endif + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// Constructor +/////////////////////////////////////////////////////////////////////////////// +Environment::Environment(Options& opt): + options(opt), + log(cout), + winSys(opt) +{ +# if defined(__UNIX__) + + // If running tests, first create the results directory. + // Refuse to overwrite one that already exists. + if (opt.mode == Options::run) { + if (mkdir(opt.db1Name.c_str(), 0755)) { + if (errno == EEXIST) + throw DBExists(); + else + throw DBCantOpen(opt.db1Name); + } + // If comparing previous runs, make a token attempt to verify + // that the two databases exist. + } else { + struct stat s; + if (stat(opt.db1Name.c_str(), &s) || !S_ISDIR(s.st_mode)) + throw DBCantOpen(opt.db1Name); + if (stat(opt.db2Name.c_str(), &s) || !S_ISDIR(s.st_mode)) + throw DBCantOpen(opt.db2Name); + } + +# elif defined(__MS__) + // If running tests, first create the results directory. + // Refuse to overwrite one that already exists. + if (opt.mode == Options::run) { + if (!CreateDirectory(opt.db1Name.c_str(),0)) { + if (GetLastError() == ERROR_ALREADY_EXISTS) + throw DBExists(); + else + throw DBCantOpen(opt.db1Name); + } + // If comparing previous runs, make a token attempt to verify + // that the two databases exist. + } else { + struct _stat s; + + if (_stat(opt.db1Name.c_str(), &s) || !(s.st_mode & _S_IFDIR)) + throw DBCantOpen(opt.db1Name); + if (_stat(opt.db2Name.c_str(), &s) || !(s.st_mode & _S_IFDIR)) + throw DBCantOpen(opt.db2Name); + } + +# endif +} // Environment::Environment() + +/////////////////////////////////////////////////////////////////////////////// +// Results-file access utilities +/////////////////////////////////////////////////////////////////////////////// +string +Environment::resultFileName(string& dbName, string& testName) { +# if defined(__UNIX__) + string dirName(dbName + '/' + testName); + if (mkdir(dirName.c_str(), 0755)) { + if (errno != EEXIST) + throw DBCantOpen(dirName); + } + string fileName(dirName + "/results"); +# elif defined(__MS__) + string dirName(dbName + '/' + testName); + if (!CreateDirectory(dirName.c_str(),0)) { + if (GetLastError() != ERROR_ALREADY_EXISTS) + throw DBCantOpen(dirName); + } + string fileName(dirName + "/results"); +# endif + return fileName; +} // Environment::resultFileName + +string +Environment::imageFileName(string& dbName, string& testName, int n) { + char sn[4]; + sn[3] = 0; + sn[2] = static_cast<char>('0' + n % 10); + sn[1] = static_cast<char>('0' + (n / 10) % 10); + sn[0] = static_cast<char>('0' + (n / 100) % 10); +# if defined(__UNIX__) + string fileName(dbName + '/' + testName + "/i" + sn + ".tif"); +# elif defined(__MS__) + string fileName(dbName + '/' + testName + "/i" + sn + ".tif"); +# endif + return fileName; +} // Environment::imageFileName + +void +Environment::quiesce() { + winSys.quiesce(); +# if defined(__UNIX__) + sync(); +# elif defined(__MS__) +# endif +} // Environment::quiesce + +} // namespace GLEAN diff --git a/src/glean/environ.h b/src/glean/environ.h new file mode 100644 index 0000000..ed51384 --- /dev/null +++ b/src/glean/environ.h @@ -0,0 +1,111 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// environ.h: global test environment + +// This class provides a facade for all the operating-system and +// window-system services that we need to run ``portable'' tests. +// Examples include logging services, opening streams to read or write +// database files, and gaining access to the window system. + + +#ifndef __environ_h__ +#define __environ_h__ + +#include <iostream> +#include "options.h" +#include "winsys.h" + +namespace GLEAN { + +class Image; // Forward and mutually-recursive references. + +class Environment { + public: + // Constructors: + Environment(Options& opt); + + // Exceptions: + struct Error { }; // Base class for all errors. + struct DBExists: public Error { // Output DB already exists. + }; + struct DBCantOpen: public Error { // Can't open a DB. + const string* db; + DBCantOpen(string& s) {db = &s;} + }; + + // Members: + Options options; // Global testing options. + + ostream& log; // Output stream used for logging results. + + WindowSystem winSys; // The window system providing the OpenGL + // implementation under test. + + string resultFileName(string& dbName, string& testName); + // Return name of results file for given + // test. Suitable for opening a stream. + // XXX Creates results directory as a side + // effect. Should separate this. + inline string resultFileName(string& testName) { + return resultFileName(options.db1Name, testName); + } + inline string result1FileName(string& testName) { + return resultFileName(options.db1Name, testName); + } + inline string result2FileName(string& testName) { + return resultFileName(options.db2Name, testName); + } + + string imageFileName(string& dbName, string& testName, int n); + // Return name of image file number ``n'' + // associated with the given test. Suitable + // for use with Image::readTIFF(), etc. + // XXX Doesn't create results directory, + // so resultFileName() must be called before + // using this. + inline string imageFileName(string& testName, int n) { + return imageFileName(options.db1Name, testName, n); + } + inline string image1FileName(string& testName, int n) { + return imageFileName(options.db1Name, testName, n); + } + inline string image2FileName(string& testName, int n) { + return imageFileName(options.db2Name, testName, n); + } + + void quiesce(); // Settle down before starting a benchmark. + +}; // class Environment + +} // namespace GLEAN + +#endif // __environ_h__ diff --git a/src/glean/geomutil.cpp b/src/glean/geomutil.cpp new file mode 100644 index 0000000..0ec0f1c --- /dev/null +++ b/src/glean/geomutil.cpp @@ -0,0 +1,52 @@ +// geomutil.cpp: frequently-used geometric operations + +#include "geomutil.h" +#include "rand.h" + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// RandomMesh2D: Generate 2D array with fixed boundaries but interior points +// that have been perturbed randomly. +/////////////////////////////////////////////////////////////////////////////// +RandomMesh2D::RandomMesh2D(float minX, float maxX, int xPoints, + float minY, float maxY, int yPoints, + RandomDouble& rand) { + m = new float[xPoints * yPoints * 2]; + rowLength = xPoints; + + for (int iy = 0; iy < yPoints; ++iy) { + double sum = 0.0; + int ix; + for (ix = 0; ix < xPoints; ++ix) { + (*this)(iy, ix)[0] = sum; + sum += rand.next(); + } + double scale = (maxX - minX) / (*this)(iy, xPoints - 1)[0]; + for (ix = 0; ix < xPoints; ++ix) { + float* x = (*this)(iy, ix) + 0; + *x = scale * *x + minX; + } + } + + for (int ix = 0; ix < xPoints; ++ix) { + double sum = 0.0; + int iy; + for (iy = 0; iy < yPoints; ++iy) { + (*this)(iy, ix)[1] = sum; + sum += rand.next(); + } + double scale = (maxY - minY) / (*this)(yPoints - 1, ix)[1]; + for (iy = 0; iy < yPoints; ++iy) { + float* y = (*this)(iy, ix) + 1; + *y = scale * *y + minY; + } + } +} // RandomMesh2D::RandomMesh2D + +RandomMesh2D::~RandomMesh2D() { + delete[] m; +} // RandomMesh2D::~RandomMesh2D + + +} // namespace GLEAN diff --git a/src/glean/geomutil.h b/src/glean/geomutil.h new file mode 100644 index 0000000..df476ba --- /dev/null +++ b/src/glean/geomutil.h @@ -0,0 +1,55 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// geomutil.h: frequently-used geometric operations + +#ifndef __geomutil_h__ +#define __geomutil_h__ + +namespace GLEAN { + +class RandomDouble; // Forward reference. + +class RandomMesh2D { + float* m; + int rowLength; + public: + RandomMesh2D(float minX, float maxX, int xPoints, + float minY, float maxY, int yPoints, + RandomDouble& rand); + ~RandomMesh2D(); + inline float* operator() (int y, int x) + { return m + 2 * (y * rowLength + x); } +}; // RandomMesh2D + +} // namespace GLEAN + +#endif // __geomutil_h__ diff --git a/src/glean/glutils.cpp b/src/glean/glutils.cpp new file mode 100644 index 0000000..5c87789 --- /dev/null +++ b/src/glean/glutils.cpp @@ -0,0 +1,60 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// glutils.cpp: frequently-used OpenGL operations + +#include "glwrap.h" +#include "environ.h" + +#include "glutils.h" + +namespace GLEAN { + +namespace GLUtils { + +/////////////////////////////////////////////////////////////////////////////// +// useScreenCoords: Map object coords directly to screen coords. +/////////////////////////////////////////////////////////////////////////////// +void +useScreenCoords(int windowW, int windowH) { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, windowW, 0, windowH, -1, 1); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glViewport(0, 0, windowW, windowH); +} // useScreenCoords + +} // namespace GLUtils + +} // namespace GLEAN diff --git a/src/glean/glutils.h b/src/glean/glutils.h new file mode 100644 index 0000000..84e6b7a --- /dev/null +++ b/src/glean/glutils.h @@ -0,0 +1,52 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// glutils.h: frequently-used OpenGL operations + +#ifndef __glutils_h__ +#define __glutils_h__ + +namespace GLEAN { + +class Environment; // Forward reference. + +namespace GLUtils { + +// Set up projection and modelview matrices so that first-quadrant +// object coordinates map directly to screen coordinates (using the +// normal Cartesian convention, with (0,0) at lower left). +void useScreenCoords(int windowW, int windowH); + +} // namespace GLUtils + +} // namespace GLEAN + +#endif // __glutils_h__ diff --git a/src/glean/main.cpp b/src/glean/main.cpp new file mode 100644 index 0000000..5eeaeed --- /dev/null +++ b/src/glean/main.cpp @@ -0,0 +1,290 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// main.cpp: main program for Glean + +#include <assert.h> +#include <iostream> +#include <string> +#include <vector> +#include <algorithm> +#include "options.h" +#include "dsfilt.h" +#include "environ.h" +#include "test.h" +#include "version.h" +#include "lex.h" + +using namespace std; +using namespace GLEAN; + +char* mandatoryArg(int argc, char* argv[], int i); +void selectTests(Options& o, vector<string>& allTestNames, int argc, + char* argv[], int i); +void usage(char* command); + +int +main(int argc, char* argv[]) { + + // Until someone gets around to writing a fancy GUI front-end, + // we'll set options the old-fashioned way. + Options o; + + vector<string> allTestNames; + for (Test* t = Test::testList; t; t = t->nextTest) + allTestNames.push_back(t->name); + sort(allTestNames.begin(), allTestNames.end()); + o.selectedTests = allTestNames; + + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--help")) + usage(argv[0]); + else if (!strcmp(argv[i], "-v") + || !strcmp(argv[i], "--verbose")) { + ++o.verbosity; + } else if (!strcmp(argv[i], "-r") + || !strcmp(argv[i], "--run")) { + o.mode = Options::run; + ++i; + o.db1Name = mandatoryArg(argc, argv, i); + } else if (!strcmp(argv[i], "-c") + || !strcmp(argv[i], "--compare")) { + o.mode = Options::compare; + ++i; + o.db1Name = mandatoryArg(argc, argv, i); + ++i; + o.db2Name = mandatoryArg(argc, argv, i); + } else if (!strcmp(argv[i], "--visuals")) { + ++i; + o.visFilter = mandatoryArg(argc, argv, i); + } else if (!strcmp(argv[i], "-t") + || !strcmp(argv[i], "--tests")) { + ++i; + selectTests(o, allTestNames, argc, argv, i); +# if defined(__X11__) + } else if (!strcmp(argv[i], "-display") + || !strcmp(argv[i], "--display")) { + ++i; + o.dpyName = mandatoryArg(argc, argv, i); +# endif + } else + usage(argv[0]); + } + + if (o.mode == Options::notSet) + usage(argv[0]); + + // Create the test environment, then invoke each test to generate + // results or compare two previous runs. + try { + Environment e(o); + switch (o.mode) { + case Options::run: + { + for (Test* t = Test::testList; t; t = t->nextTest) + if (binary_search(o.selectedTests.begin(), + o.selectedTests.end(), t->name)) + t->run(e); + break; + } + case Options::compare: + { + for (Test* t = Test::testList; t; t = t->nextTest) + if (binary_search(o.selectedTests.begin(), + o.selectedTests.end(), t->name)) + t->compare(e); + break; + } + default: + cerr << "Bad run mode in main()\n"; + break; + } + } +#if defined(__X11__) + catch (WindowSystem::CantOpenDisplay) { + cerr << "can't open display " << o.dpyName << "\n"; + exit(1); + } +#endif + catch (WindowSystem::NoOpenGL) { + cerr << "display doesn't support OpenGL\n"; + exit(1); + } + catch (DrawingSurfaceFilter::Syntax e) { + cerr << "Syntax error in visual selection criteria:\n" + "'" << o.visFilter << "'\n"; + for (int i = 0; i < e.position; ++i) + cerr << ' '; + cerr << "^ " << e.err << '\n'; + exit(1); + } + catch (Environment::DBExists) { + cerr << "Won't overwrite existing database " << o.db1Name + << "\n"; + exit(1); + } + catch (Environment::DBCantOpen e) { + cerr << "Can't open database directory " << *e.db << "\n"; + exit(1); + } + catch (Test::CantOpenResultsFile e) { + cerr << "Can't open results file for test " << e.testName + << " in database " << e.dbName << '\n'; + exit(1); + } + catch (...) { + cerr << "caught an unexpected error in main()\n"; + exit(1); + } + + return 0; +} // main + + +char* +mandatoryArg(int argc, char* argv[], int i) { + if (i < argc && argv[i][0] != '-') + return argv[i]; + usage(argv[0]); + /*NOTREACHED*/ + return 0; +} // mandatoryArg + + +void +selectTests(Options& o, vector<string>& allTestNames, int argc, char* argv[], + int i) { + if (i >= argc) + usage(argv[0]); + + // At present, we deal with the following syntax: + // [+] testname {(+|-) testname} + // Assume we're running none of the tests, then include + // those preceded by "+" and exclude those preceded by + // "-". + // - testname {(+|-) testname} + // Assume we're running all of the tests, then exclude + // those preceded by "-" and include those preceded by + // "+". + // XXX It would be nice to support the syntax "@filename" to mean + // "the list of tests given in the named file." This could be + // preceded by "+" or "-" just like an ordinary test name, or maybe + // the +/- should be required in the file itself. + + struct SyntaxError { + int position; + SyntaxError(int pos): position(pos) { } + }; + + Lex lex(argv[i]); + try { + lex.next(); + if (lex.token == Lex::MINUS) + o.selectedTests = allTestNames; + else + o.selectedTests.resize(0); + + while (lex.token != Lex::END) { + bool inserting = true; + if (lex.token == Lex::MINUS) { + inserting = false; + lex.next(); + } else if (lex.token == Lex::PLUS) + lex.next(); + + if (lex.token != Lex::ID) + throw SyntaxError(lex.position()); + + if (!binary_search(allTestNames.begin(), + allTestNames.end(), lex.id)) + cerr << "Warning: " << lex.id << " ignored;" + " not a valid test name.\n"; + else { + vector<string>::iterator p = + lower_bound(o.selectedTests.begin(), + o.selectedTests.end(), lex.id); + if (inserting) { + if (p == o.selectedTests.end() + || *p != lex.id) + o.selectedTests.insert(p, + lex.id); + } else { + // removing + if (p != o.selectedTests.end() + && *p == lex.id) + o.selectedTests.erase(p); + } + } + lex.next(); + } + } + catch (Lex::Lexical e) { + cerr << "Lexical error in test inclusion/exclusion list:\n" + "'" << argv[i] << "'\n"; + for (int i = 0; i < e.position; ++i) + cerr << ' '; + cerr << "^ " << e.err << "\n\n"; + usage(argv[0]); + } + catch (SyntaxError e) { + cerr << "'" << argv[i] << "'\n"; + for (int i = 0; i < e.position; ++i) + cerr << ' '; + cerr << "^ Syntax error in test inclusion/exclusion list\n\n"; + usage(argv[0]); + } +} // selectTests + + +void +usage(char* command) { + cerr << GLEAN::versionString << '\n'; + cerr << "Usage: " << command << " mode [options]\n" +"\n" +"mode:\n" +" (-r|--run) results-directory\n" +" or (-c|--compare) old-results-dir new-results-dir\n" +"\n" +"options:\n" +" (-v|--verbose) # each occurrence increases\n" +" # verbosity of output\n" +" --visuals 'filter-string' # select subset of visuals (FBConfigs,\n" +" # pixel formats) to test\n" +" (-t|--tests) {(+|-)test} # choose tests to include (+) or exclude (-)\n" +" --help # display usage information\n" +#if defined(__X11__) +" -display X11-display-name # select X11 display to use\n" +" (or --display)\n" +#elif defined(__WIN__) +#endif + ; + exit(1); +} // usage diff --git a/src/glean/makefile.win b/src/glean/makefile.win new file mode 100644 index 0000000..2ad3944 --- /dev/null +++ b/src/glean/makefile.win @@ -0,0 +1,132 @@ +.SILENT : + +!IF "$(CFG)" == "" +CFG=release +#!MESSAGE No configuration specified. Defaulting to release build. +!ENDIF + +!IF "$(CFG)" != "release" && "$(CFG)" != "debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "difftiff.mak" CFG="release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "release" +!MESSAGE "debug" +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!INCLUDE $(GLEAN_ROOT)\make\common.win + +LINK32_OBJS= "$(INTDIR)\dsurf.obj" \ + "$(INTDIR)\environ.obj" \ + "$(INTDIR)\geomutil.obj" \ + "$(INTDIR)\glutils.obj" \ + "$(INTDIR)\main.obj" \ + "$(INTDIR)\misc.obj" \ + "$(INTDIR)\options.obj" \ + "$(INTDIR)\rc.obj" \ + "$(INTDIR)\tbasic.obj" \ + "$(INTDIR)\tblend.obj" \ + "$(INTDIR)\tchgperf.obj" \ + "$(INTDIR)\test.obj" \ + "$(INTDIR)\tgetstr.obj" \ + "$(INTDIR)\trgbtris.obj" \ + "$(INTDIR)\winsys.obj" + +LIBS=dsurf.lib image.lib stats.lib timer.lib lex.lib libtiff.lib opengl32.lib glu32.lib glu32.lib glut32.lib kernel32.lib user32.lib gdi32.lib + +FTARGET=glean +TARGET=$(FTARGET).exe + +!IF "$(CFG)" == "release" + +DEFINES=$(DEFINES) /D "NDEBUG" + +OUTDIR=.\Release +INTDIR=.\Release + +ALL : "$(GLEAN_BIN_DIR)\$(TARGET)" + +CLEAN : + -@erase "$(INTDIR)\*.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(OUTDIR)\$(FTARGET).pch + -@deltree /Y "$(OUTDIR)" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /ML /W3 /GX /O2 $(DEFINES) $(INCLUDE_DIRS) /Fp"$(INTDIR)\$(FTARGET).pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +LINK32=link.exe +LINK32_FLAGS=$(LIBS) /nologo /subsystem:console /machine:I386 /include:"__imp__glGetString@4" /out:"$(GLEAN_BIN_DIR)\$(TARGET)" /nodefaultlib:libcd.lib + +"$(GLEAN_BIN_DIR)\$(TARGET)" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) $(LINK32_FLAGS) $(LINK32_OBJS) $(LIB_DIRS) + + +!ELSEIF "$(CFG)" == "debug" + +DEFINES=$(DEFINES) /D "_DEBUG" + +OUTDIR=.\Debug +INTDIR=.\Debug + +ALL : "$(GLEAN_BIN_DIR)\$(TARGET)" + +CLEAN : + -@erase "$(INTDIR)\*.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(INTDIR)\vc60.pdb" + -@erase "$(OUTDIR)\$(FTARGET).pdb" + -@erase "$(GLEAN_BIN_DIR)\$(FTARGET).ilk" + -@erase "$(OUTDIR)\$(FTARGET).pch + -@deltree /Y "$(OUTDIR)" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MLd /W3 /Gm /GX /ZI /Od $(DEFINES) $(INCLUDE_DIRS) /Fp"$(INTDIR)\$(FTARGET).pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +LINK32=link.exe +LINK32_FLAGS=$(LIBS) /nologo /subsystem:console /incremental:yes /pdb:"$(OUTDIR)\$(FTARGET).pdb" /debug /machine:I386 /include:"__imp__glGetString@4" /out:"$(GLEAN_BIN_DIR)\$(TARGET)" /pdbtype:sept $(LIB_DIRS) + +"$(GLEAN_BIN_DIR)\$(TARGET)" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) $(LINK32_FLAGS) $(LINK32_OBJS) + +!ENDIF + + diff --git a/src/glean/misc.cpp b/src/glean/misc.cpp new file mode 100644 index 0000000..3bcb0ea --- /dev/null +++ b/src/glean/misc.cpp @@ -0,0 +1,59 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// misc.cpp: implementation of miscellaneous functions + +#include <cctype> +#include "misc.h" + +namespace GLEAN { + + +/////////////////////////////////////////////////////////////////////////////// +// Utility routine to skip whitespace in streams +// This is helpful when interleaving invocations of getline() and +// operator>>. In particular, after operator>> at the end of a line, +// there may be whitespace (especially a newline) remaining; this may +// confuse a subsequent invocation of getline(). +/////////////////////////////////////////////////////////////////////////////// +void +SkipWhitespace(istream& s) { + char c; + while (s.get(c)) { + if (!isspace(c)) { + s.putback(c); + break; + } + } +} // SkipWhitespace + + +} // namespace GLEAN diff --git a/src/glean/misc.h b/src/glean/misc.h new file mode 100644 index 0000000..24d0485 --- /dev/null +++ b/src/glean/misc.h @@ -0,0 +1,48 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// misc.h: Miscellaneous functions + + +#ifndef __misc_h__ +#define __misc_h__ + +#include <iostream> + +using namespace std; + +namespace GLEAN { + +void SkipWhitespace(istream& s); + +} // namespace GLEAN + +#endif // __misc_h__ diff --git a/src/glean/options.cpp b/src/glean/options.cpp new file mode 100644 index 0000000..e00cb0b --- /dev/null +++ b/src/glean/options.cpp @@ -0,0 +1,55 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// options.cpp: implementation of global options class + +#include "options.h" + +namespace GLEAN { + + + +Options::Options() { + mode = notSet; + verbosity = 0; + db1Name = "results"; + db2Name = "previous"; + visFilter = "1"; + selectedTests.resize(0); +# if defined(__X11__) + dpyName = ":0"; +# elif defined(__WIN__) +# endif +} // Options::Options() + + + +} // namespace GLEAN diff --git a/src/glean/options.h b/src/glean/options.h new file mode 100644 index 0000000..88aacd1 --- /dev/null +++ b/src/glean/options.h @@ -0,0 +1,95 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// options.h: global test options + +// This class encapsulates global options that apply to the entire +// testing process -- things like the display name (for X11), +// constraints on the drawing surface configurations to be tested, +// locations of test results files, etc. + +// We collect this information for two reasons. First, it allows the +// (relatively) large number of parameters needed for creating an +// Environment to be passed cleanly to Environment's constructor. +// Second, it allows the process of gathering parameters (by parsing a +// command line, running a set of GUI dialogs, etc.) to be separated +// from the creation of the Environment. + + + +#ifndef __options_h__ +#define __options_h__ + +#include <string> +#include <vector> + +using namespace std; + +namespace GLEAN { + +class Options { + public: + typedef enum {notSet, run, compare} RunMode; + RunMode mode; // Indicates whether we're generating + // results, or comparing two previous runs. + + int verbosity; // Verbosity level. 0 == concise; larger + // values imply more verbose output. + + string db1Name; // Name of output database, or one of + // the two databases being compared. + // Typically the pathname of a directory, + // provided on the command line. + + string db2Name; // Name of the second database being + // compared. + + string visFilter; // Filter constraining the set of visuals + // (FBConfigs, pixel formats) that will be + // available for test. See + // DrawingSurfaceFilter for description of + // contents. + + vector<string> selectedTests; + // Sorted list of tests to be executed. + +#if defined(__X11__) + string dpyName; // Name of the X11 display providing the + // OpenGL implementation to be tested. +#elif defined(__WIN__) +#endif + + Options(); +}; // class Options + +} // namespace GLEAN + +#endif // __options_h__ diff --git a/src/glean/rc.cpp b/src/glean/rc.cpp new file mode 100644 index 0000000..0172924 --- /dev/null +++ b/src/glean/rc.cpp @@ -0,0 +1,150 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// rc.cpp: implementation of rendering context utilities + +#include <iostream> +#include <algorithm> +#include "rc.h" +#include "dsconfig.h" +#include "winsys.h" + +namespace { + +#if defined (__WIN__) + +// XXX +// wglCreateContext requires a handle to a device context. +// The ctor of RenderingContext doesn't know which window +// it is creating a surface for, only what the pixelformat +// of that window is. The hDC passed to wglCreateContext +// doesn't have to be the same as the one use in SwapBuffers +// or wglMakeCurrent, their pixelformats have to be the +// same though. A limitation is that the pixelformat of +// a window can only be set once. That is why a +// temporary window is created. + + +HGLRC create_context(GLEAN::DrawingSurfaceConfig &c) +{ + HWND hwnd = CreateWindow("STATIC","temp",WS_POPUP, + CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT, + 0,0,GetModuleHandle(NULL),0); + + if (!hwnd) + return 0; + + HDC hDC = GetDC(hwnd); + if (!hDC) + return 0; + + PIXELFORMATDESCRIPTOR pfd; + + if (!SetPixelFormat(hDC,c.pfdID,&pfd)) + { + ReleaseDC(hwnd,hDC); + DestroyWindow(hwnd); + return 0; + } + + HGLRC rc = wglCreateContext(hDC); + if (!rc) + return 0; + + ReleaseDC(hwnd,hDC); + DestroyWindow(hwnd); + + return rc; +} + +#endif + +} // anonymous namespace + + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors +/////////////////////////////////////////////////////////////////////////////// +RenderingContext::RenderingContext(WindowSystem& ws, DrawingSurfaceConfig& c, + RenderingContext* share, bool direct) { + + // Link back to enclosing window system, so as to simplify bookkeeping: + winSys = &ws; + ws.contexts.push_back(this); + +# if defined(__X11__) + +# if defined(GLX_VERSION_1_3) + +# error "XXX Need GLX 1.3 rc constructor code" + +# else + + // Create the rendering context: + rc = glXCreateContext(winSys->dpy, c.vi, (share? share->rc: 0), + direct? True: False); + if (!rc) + throw Error(); + // XXX Ideally, we would deal with X11 and GLX errors here, too + // (Badmatch, BadValue, GLXBadContext, BadAlloc) + +# endif + +# elif defined(__WIN__) + + rc = create_context(c); + if (!rc) + throw Error(); +# endif + +} // RenderingContext::RenderingContext + +/////////////////////////////////////////////////////////////////////////////// +// Destructors +/////////////////////////////////////////////////////////////////////////////// + +RenderingContext::~RenderingContext() { + remove(winSys->contexts.begin(), winSys->contexts.end(), this); +# if defined(__X11__) +# if defined(GLX_VERSION_1_3) +# error "Need to write GLX 1.3 rc destructor" +# else + glXDestroyContext(winSys->dpy, rc); +# endif +# elif defined(__WIN__) + wglDeleteContext(rc); +# endif +} // RenderingContext::~RenderingContext + + +} // namespace GLEAN diff --git a/src/glean/rc.h b/src/glean/rc.h new file mode 100644 index 0000000..e97707d --- /dev/null +++ b/src/glean/rc.h @@ -0,0 +1,66 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// rc.h: utilities for manipulating rendering contexts + +#ifndef __rc_h__ +#define __rc_h__ + +#include "glwrap.h" + +namespace GLEAN { + +class WindowSystem; // Forward and mutually-recursive references +class DrawingSurfaceConfig; + +class RenderingContext { + public: + RenderingContext(WindowSystem& ws, DrawingSurfaceConfig& c, + RenderingContext* share = 0, bool direct = true); + ~RenderingContext(); + + // Exceptions: + struct Error { }; // Base class for all errors. + + // Members: + WindowSystem* winSys; // Window system that owns this context. + +# if defined(__X11__) + GLXContext rc; +# elif defined(__WIN__) + ::HGLRC rc; +# endif + +}; // class RenderingContext + +} // namespace GLEAN + +#endif // __rc_h__ diff --git a/src/glean/tbasic.cpp b/src/glean/tbasic.cpp new file mode 100644 index 0000000..bb83dbd --- /dev/null +++ b/src/glean/tbasic.cpp @@ -0,0 +1,254 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// tbasic.cpp: implementation of example class for basic tests + +#ifdef __UNIX__ +#include <unistd.h> +#endif + +#include <iostream> +#include <fstream> +#include "dsconfig.h" +#include "dsfilt.h" +#include "dsurf.h" +#include "winsys.h" +#include "environ.h" +#include "rc.h" +#include "glutils.h" +#include "tbasic.h" +#include "misc.h" + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// Constructor/Destructor: +/////////////////////////////////////////////////////////////////////////////// +BasicTest::BasicTest(const char* aName, const char* aFilter, + const char* aDescription): + Test(aName), filter(aFilter), description(aDescription) { +} // BasicTest::BasicTest() + +BasicTest::~BasicTest() { +} // BasicTest::~BasicTest + +/////////////////////////////////////////////////////////////////////////////// +// run: run tests, save results in a vector and in the results file +/////////////////////////////////////////////////////////////////////////////// +void +BasicTest::run(Environment& environment) { + // Guard against multiple invocations: + if (hasRun) + return; + + // Set up environment for use by other functions: + env = &environment; + + // Document the test in the log, if requested: + logDescription(); + + // Compute results and make them available to subsequent tests: + WindowSystem& ws = env->winSys; + try { + // Open the results file: + OutputStream os(*this); + + // Select the drawing configurations for testing: + DrawingSurfaceFilter f(filter); + vector<DrawingSurfaceConfig*> configs(f.filter(ws.surfConfigs)); + + // Test each config: + for (vector<DrawingSurfaceConfig*>::const_iterator + p = configs.begin(); p < configs.end(); ++p) { + Window w(ws, **p, 258, 258); + RenderingContext rc(ws, **p); + if (!ws.makeCurrent(rc, w)) + ; // XXX need to throw exception here + + // Create a result object and run the test: + Result *r = new Result; + r->config = *p; + runOne(*r); + + // Save the result locally and in the results file: + results.push_back(r); + r->put(os); + } + } + catch (DrawingSurfaceFilter::Syntax e) { + env->log << "Syntax error in test's drawing-surface selection" + "criteria:\n'" << filter << "'\n"; + for (int i = 0; i < e.position; ++i) + env->log << ' '; + env->log << "^ " << e.err << '\n'; + } + catch (RenderingContext::Error) { + env->log << "Could not create a rendering context\n"; + } + + env->log << '\n'; + + // Note that we've completed the run: + hasRun = true; +} + +void +BasicTest::compare(Environment& environment) { + // Save the environment for use by other member functions: + env = &environment; + + // Display the description if needed: + logDescription(); + + // Read results from previous runs: + Input1Stream is1(*this); + vector<Result*> oldR(getResults(is1)); + Input2Stream is2(*this); + vector<Result*> newR(getResults(is2)); + + // Construct a vector of surface configurations from the old run. + // (Later we'll find the best match in this vector for each config + // in the new run.) + vector<DrawingSurfaceConfig*> oldConfigs; + for (vector<Result*>::const_iterator p = oldR.begin(); p < oldR.end(); + ++p) + oldConfigs.push_back((*p)->config); + + // Compare results: + for (vector<Result*>::const_iterator newP = newR.begin(); + newP < newR.end(); ++newP) { + + // Find the drawing surface config that most closely matches + // the config for this result: + int c = (*newP)->config->match(oldConfigs); + + // If there was a match, compare the results: + if (c < 0) + env->log << name << ": NOTE no matching config for " << + (*newP)->config->conciseDescription() << '\n'; + else + compareOne(*(oldR[c]), **newP); + } + + // Get rid of the results; we don't need them for future comparisons. + for (vector<Result*>::iterator op = newR.begin(); op < newR.end(); ++op) + delete *op; + for (vector<Result*>::iterator np = oldR.begin(); np < oldR.end(); ++np) + delete *np; +} + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +BasicTest::runOne(Result& r) { + r.pass = true; + env->log << name << (r.pass? ": PASS ": ": FAIL ") + << r.config->conciseDescription() << '\n'; +} // BasicTest::runOne + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +BasicTest::compareOne(Result& oldR, Result& newR) { + if (oldR.pass == newR.pass) { + if (env->options.verbosity) + env->log << name << ": SAME " + << newR.config->conciseDescription() << '\n' + << (oldR.pass? "\tBoth PASS\n": "\tBoth FAIL\n") + ; + } else { + env->log << name << ": DIFF " + << newR.config->conciseDescription() << '\n' + << '\t' << env->options.db1Name + << (oldR.pass? " PASS, ": " FAIL, ") + << env->options.db2Name + << (newR.pass? " PASS\n": " FAIL\n"); + } +} // BasicTest::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// logDescription: Print description on the log file, according to the +// current verbosity level. +/////////////////////////////////////////////////////////////////////////////// +void +BasicTest::logDescription() { + if (env->options.verbosity) + env->log << +"----------------------------------------------------------------------\n" + << description << '\n'; +} // BasicTest::logDescription + +/////////////////////////////////////////////////////////////////////////////// +// Result I/O functions: +/////////////////////////////////////////////////////////////////////////////// +void +BasicTest::Result::put(ostream& s) const { + s << config->canonicalDescription() << '\n' << pass << '\n'; +} // BasicTest::Result::put + +bool +BasicTest::Result::get(istream& s) { + SkipWhitespace(s); + string configDesc; + if (!getline(s, configDesc)) + return false; + config = new DrawingSurfaceConfig(configDesc); + s >> pass; + return s.good(); +} // BasicTest::Result::get + +vector<BasicTest::Result*> +BasicTest::getResults(istream& s) { + vector<Result*> v; + while (s.good()) { + Result* r = new Result(); + if (r->get(s)) + v.push_back(r); + else { + delete r; + break; + } + } + + return v; +} // BasicTest::getResults + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +BasicTest basicTest("basic", "window", + "This trivial test simply verifies the internal support for basic\n" + "tests. It is run on every OpenGL-capable drawing surface\n" + "configuration that supports creation of a window.\n"); + +} // namespace GLEAN diff --git a/src/glean/tbasic.h b/src/glean/tbasic.h new file mode 100644 index 0000000..9922c5b --- /dev/null +++ b/src/glean/tbasic.h @@ -0,0 +1,97 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// tbasic.h: Example class for basic tests + +// This class (derived from Test) provides a framework for a large set +// of correctness tests that should be portable (in the sense that +// they don't contain OS- or window-system-specific code). + +// Each basic test includes a drawing surface filter string. The test +// will be run on all the drawing surface configurations that are +// selected by the filter, and one result structure will be generated +// for each such configuration. + +// When comparing two runs, the drawing surface configurations are +// used to select plausible matches among the results. + + +#ifndef __tbasic_h__ +#define __tbasic_h__ + +#include "test.h" + +class DrawingSurfaceConfig; // Forward reference. + +namespace GLEAN { + +class BasicTest: public Test { + public: + BasicTest(const char* testName, const char* filter, + const char* description); + virtual ~BasicTest(); + + const char* filter; // Drawing surface configuration filter. + const char* description; // Verbose description of test. + + virtual void run(Environment& env); // Run test, save results. + + virtual void compare(Environment& env); + // Compare two previous runs. + + // Class for a single test result. All basic tests have a + // drawing surface configuration, plus other information + // that's specific to the test. + class Result: public Test::Result { + public: + DrawingSurfaceConfig* config; + bool pass; + + virtual void put(ostream& s) const; + virtual bool get(istream& s); + + Result() { } + virtual ~Result() { } + }; + + vector<Result*> results; + + virtual void runOne(Result& r); + virtual void compareOne(Result& oldR, Result& newR); + virtual vector<Result*> getResults(istream& s); + + void logDescription(); + +}; // class BasicTest + +} // namespace GLEAN + +#endif // __tbasic_h__ diff --git a/src/glean/tblend.cpp b/src/glean/tblend.cpp new file mode 100644 index 0000000..a7f2638 --- /dev/null +++ b/src/glean/tblend.cpp @@ -0,0 +1,768 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// tblend.cpp: Test blending functions. + +#ifdef __UNIX__ +#include <unistd.h> +#endif + +#include <iostream> +#include <fstream> +#include <algorithm> +#include <cmath> +#include "dsconfig.h" +#include "dsfilt.h" +#include "dsurf.h" +#include "winsys.h" +#include "environ.h" +#include "rc.h" +#include "glutils.h" +#include "geomutil.h" +#include "rand.h" +#include "stats.h" +#include "image.h" +#include "tblend.h" +#include "misc.h" + +namespace { + +struct factorNameMapping {GLenum factor; char* name;}; +factorNameMapping factorNames[] = { + {GL_DST_ALPHA, "GL_DST_ALPHA"}, + {GL_DST_COLOR, "GL_DST_COLOR"}, + {GL_ONE, "GL_ONE"}, + {GL_ONE_MINUS_DST_ALPHA, "GL_ONE_MINUS_DST_ALPHA"}, + {GL_ONE_MINUS_DST_COLOR, "GL_ONE_MINUS_DST_COLOR"}, + {GL_ONE_MINUS_SRC_ALPHA, "GL_ONE_MINUS_SRC_ALPHA"}, + {GL_ONE_MINUS_SRC_COLOR, "GL_ONE_MINUS_SRC_COLOR"}, + {GL_SRC_ALPHA, "GL_SRC_ALPHA"}, + {GL_SRC_ALPHA_SATURATE, "GL_SRC_ALPHA_SATURATE"}, + {GL_SRC_COLOR, "GL_SRC_COLOR"}, + {GL_ZERO, "GL_ZERO"} +}; + +char* +factorToName(GLenum factor) { + for (unsigned int i = 0; + i < sizeof(factorNames) / sizeof(factorNames[0]); + ++i) + if (factorNames[i].factor == factor) + return factorNames[i].name; + return 0; +} // factorToName + +GLenum +nameToFactor(string& name) { + for (unsigned int i = 0; + i < sizeof(factorNames) / sizeof(factorNames[0]); + ++i) + if (factorNames[i].name == name) + return factorNames[i].factor; + return GL_ZERO; +} // nameToFactor + +const int drawingSize = 64; // We will check each pair of blend factors + // for each pixel in a square image of this + // dimension, so if you make it too large, + // the tests may take quite a while to run. + +bool +needsDstAlpha(const GLenum func) { + return func == GL_DST_ALPHA || func == GL_ONE_MINUS_DST_ALPHA + || func == GL_SRC_ALPHA_SATURATE; +} + +void +makeRGBA(GLEAN::RandomBitsDouble& rRand, + GLEAN::RandomBitsDouble& gRand, + GLEAN::RandomBitsDouble& bRand, + GLEAN::RandomBitsDouble& aRand, + float* rgba) { + rgba[0] = rRand.next(); + rgba[1] = gRand.next(); + rgba[2] = bRand.next(); + rgba[3] = aRand.next(); +} // makeRGBA + +void +drawQuad(const int x, const int y, const float* color) { + glColor4fv(color); + glBegin(GL_QUADS); + glVertex2i(x, y); + glVertex2i(x + 1, y); + glVertex2i(x + 1, y + 1); + glVertex2i(x, y + 1); + glEnd(); +} // drawQuad + +inline float +clamp(float f) { + if (f < 0.0) + return 0.0; + else if (f > 1.0) + return 1.0; + else + return f; +} // clamp + +void +applyBlend(GLenum srcFactor, GLenum dstFactor, float* dst, float* src) { + // XXX Currently we don't test any of the const-color blend factors. + // It would be a good idea to do so as soon as we have access to an + // implementation that supports the OpenGL 1.2 imaging extensions. + + float sf[4]; + switch (srcFactor) { + case GL_ZERO: + sf[0] = sf[1] = sf[2] = sf[3] = 0.0; + break; + case GL_ONE: + sf[0] = sf[1] = sf[2] = sf[3] = 1.0; + break; + case GL_DST_COLOR: + sf[0] = dst[0]; sf[1] = dst[1]; sf[2] = dst[2]; sf[3] = dst[3]; + break; + case GL_ONE_MINUS_DST_COLOR: + sf[0] = 1.0 - dst[0]; sf[1] = 1.0 - dst[1]; + sf[2] = 1.0 - dst[2]; sf[3] = 1.0 - dst[3]; + break; + case GL_SRC_ALPHA: + sf[0] = sf[1] = sf[2] = sf[3] = src[3]; + break; + case GL_ONE_MINUS_SRC_ALPHA: + sf[0] = sf[1] = sf[2] = sf[3] = 1.0 - src[3]; + break; + case GL_DST_ALPHA: + sf[0] = sf[1] = sf[2] = sf[3] = dst[3]; + break; + case GL_ONE_MINUS_DST_ALPHA: + sf[0] = sf[1] = sf[2] = sf[3] = 1.0 - dst[3]; + break; + case GL_SRC_ALPHA_SATURATE: { + float f = 1.0 - dst[3]; + if (src[3] < f) + f = src[3]; + sf[0] = sf[1] = sf[2] = f; sf[3] = 1.0; + } + break; + default: + sf[0] = sf[1] = sf[2] = sf[3] = 0.0; + break; + } + + float df[4]; + switch (dstFactor) { + case GL_ZERO: + df[0] = df[1] = df[2] = df[3] = 0.0; + break; + case GL_ONE: + df[0] = df[1] = df[2] = df[3] = 1.0; + break; + case GL_SRC_COLOR: + df[0] = src[0]; df[1] = src[1]; df[2] = src[2]; df[3] = src[3]; + break; + case GL_ONE_MINUS_SRC_COLOR: + df[0] = 1.0 - src[0]; df[1] = 1.0 - src[1]; + df[2] = 1.0 - src[2]; df[3] = 1.0 - src[3]; + break; + case GL_SRC_ALPHA: + df[0] = df[1] = df[2] = df[3] = src[3]; + break; + case GL_ONE_MINUS_SRC_ALPHA: + df[0] = df[1] = df[2] = df[3] = 1.0 - src[3]; + break; + case GL_DST_ALPHA: + df[0] = df[1] = df[2] = df[3] = dst[3]; + break; + case GL_ONE_MINUS_DST_ALPHA: + df[0] = df[1] = df[2] = df[3] = 1.0 - dst[3]; + break; + default: + df[0] = df[1] = df[2] = df[3] = 0.0; + break; + } + + dst[0] = clamp(src[0] * sf[0] + dst[0] * df[0]); + dst[1] = clamp(src[1] * sf[1] + dst[1] * df[1]); + dst[2] = clamp(src[2] * sf[2] + dst[2] * df[2]); + dst[3] = clamp(src[3] * sf[3] + dst[3] * df[3]); +} // applyBlend + +struct runFactorsResult {float readbackErrorBits; float blendErrorBits;}; + +template <class T> inline T log2(T x) { return 1.4426950408889634 * log(x); } + +double +errorBits(double absError, int repBits) { + if (absError <= 0.0) + return 0.0; + double log2Error = log2(absError) + repBits; + return (log2Error < 0.0)? 0.0: log2Error; +} + +runFactorsResult +runFactors(GLenum srcFactor, GLenum dstFactor, + GLEAN::DrawingSurfaceConfig& config, GLEAN::Environment& env) { + using namespace GLEAN; + + runFactorsResult result; + int y; + + glDisable(GL_DITHER); + glClear(GL_COLOR_BUFFER_BIT); + + Image dst(drawingSize, drawingSize, GL_RGBA, GL_FLOAT); + RandomBitsDouble rRand(config.r, 6021023); + RandomBitsDouble gRand(config.g, 1137); + RandomBitsDouble bRand(config.b, 1138); + RandomBitsDouble dstARand(config.a? config.a: 1, 6); + + // Fill the framebuffer with random RGBA values, and place a copy + // in ``dst'': + glDisable(GL_BLEND); + char* dRow = dst.pixels(); + for (/*int */y = 0; y < drawingSize; ++y) { + float* pix = reinterpret_cast<float*>(dRow); + for (int x = 0; x < drawingSize; ++x) { + float rgba[4]; + makeRGBA(rRand, gRand, bRand, dstARand, rgba); + if (!config.a) + rgba[3] = 1.0; + drawQuad(x + 1, y + 1, rgba); + pix[0] = rgba[0]; + pix[1] = rgba[1]; + pix[2] = rgba[2]; + pix[3] = rgba[3]; + pix += 4; + } + dRow += dst.rowSizeInBytes(); + } + + // Read back the contents of the framebuffer, and measure any + // difference from what was actually written. We can't tell + // whether errors occurred when writing or when reading back, + // but at least we can report anything unusual. + Image fbDst(drawingSize, drawingSize, GL_RGBA, GL_FLOAT); + fbDst.read(1, 1); + Image::Registration reg1(fbDst.reg(dst)); + result.readbackErrorBits = + max(errorBits(reg1.stats[0].max(), config.r), + max(errorBits(reg1.stats[1].max(), config.g), + max(errorBits(reg1.stats[2].max(), config.b), + errorBits(reg1.stats[3].max(), config.a)))); + + // Now generate random source pixels and apply the blending + // operation to both the framebuffer and a copy in the image + // ``expected''. Note that a fresh source alpha must be + // generated here, because the range of source alpha values is + // not limited by the range of alpha values that can be + // represented in the framebuffer. Save the source pixels in + // the image ``src'' so we can diagnose any problems we find + // later. + Image expected(dst); + Image src(drawingSize, drawingSize, GL_RGBA, GL_FLOAT); + RandomBitsDouble srcARand(16, 42); + + glBlendFunc(srcFactor, dstFactor); + glEnable(GL_BLEND); + + dRow = expected.pixels(); + char* sRow = src.pixels(); + for (/*int */y = 0; y < drawingSize; ++y) { + float* pix = reinterpret_cast<float*>(dRow); + float* sPix = reinterpret_cast<float*>(sRow); + for (int x = 0; x < drawingSize; ++x) { + float rgba[4]; + makeRGBA(rRand, gRand, bRand, srcARand, rgba); + sPix[0] = rgba[0]; + sPix[1] = rgba[1]; + sPix[2] = rgba[2]; + sPix[3] = rgba[3]; + drawQuad(x + 1, y + 1, rgba); + applyBlend(srcFactor, dstFactor, pix, rgba); + pix += 4; + sPix += 4; + } + dRow += expected.rowSizeInBytes(); + sRow += src.rowSizeInBytes(); + } + + // Read the generated image (``actual'') and compare it to the + // computed image (``expected'') to see if any pixels are + // outside the expected tolerance range (one LSB). If so, + // report the first such pixel, along with the source and + // destination values that generated it. Keep track of the + // maximum error encountered. + Image actual(drawingSize, drawingSize, GL_RGBA, GL_FLOAT); + actual.read(1, 1); + result.blendErrorBits = 0.0; + sRow = actual.pixels(); + dRow = expected.pixels(); + for (/*int */y = 0; y < drawingSize; ++y) { + float* aPix = reinterpret_cast<float*>(sRow); + float* ePix = reinterpret_cast<float*>(dRow); + for (int x = 0; x < drawingSize; ++x) { + float rError = fabs(aPix[0] - ePix[0]); + float gError = fabs(aPix[1] - ePix[1]); + float bError = fabs(aPix[2] - ePix[2]); + float aError = fabs(aPix[3] - ePix[3]); + result.blendErrorBits = + max(static_cast<double>(result.blendErrorBits), + max(errorBits(rError, config.r), + max(errorBits(gError, config.g), + max(errorBits(bError, config.b), + errorBits(aError, config.a))))); + if (result.blendErrorBits > 1.0) { + if (env.options.verbosity) { +float* sPix = reinterpret_cast<float*>(src.pixels() + + y * src.rowSizeInBytes() + x * 4 * sizeof(float)); +float* dPix = reinterpret_cast<float*>(dst.pixels() + + y * dst.rowSizeInBytes() + x * 4 * sizeof(float)); +env.log << '\n' +<< "First failing pixel is at row " << y << " column " << x << "\n" +<< "Actual values are (" << aPix[0] << ", " << aPix[1] << ", " << aPix[2] + << ", " << aPix[3] << ")\n" +<< "Expected values are (" << ePix[0] << ", " << ePix[1] << ", " << ePix[2] + << ", " << ePix[3] << ")\n" +<< "Errors are (" << rError << ", " << gError << ", " << bError << ", " + << aError << ")\n" +<< "Source values are (" << sPix[0] << ", " << sPix[1] << ", " << sPix[2] + << ", " << sPix[3] << ")\n" +<< "Destination values are (" << dPix[0] << ", " << dPix[1] << ", " << dPix[2] + << ", " << dPix[3] << ")\n"; + } + return result; + } + } + sRow += actual.rowSizeInBytes(); + dRow += expected.rowSizeInBytes(); + } + + return result; +} // runOneSet + +} // anonymous namespace + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// Constructor/Destructor: +/////////////////////////////////////////////////////////////////////////////// +BlendFuncTest::BlendFuncTest(const char* aName, const char* aFilter, + const char* aDescription): + Test(aName), filter(aFilter), description(aDescription) { +} // BlendFuncTest::BlendFuncTest() + +BlendFuncTest::~BlendFuncTest() { +} // BlendFuncTest::~BlendFuncTest + +/////////////////////////////////////////////////////////////////////////////// +// run: run tests, save results in a vector and in the results file +/////////////////////////////////////////////////////////////////////////////// +void +BlendFuncTest::run(Environment& environment) { + // Guard against multiple invocations: + if (hasRun) + return; + + // Set up environment for use by other functions: + env = &environment; + + // Document the test in the log, if requested: + logDescription(); + + // Compute results and make them available to subsequent tests: + WindowSystem& ws = env->winSys; + try { + // Open the results file: + OutputStream os(*this); + + // Select the drawing configurations for testing: + DrawingSurfaceFilter f(filter); + vector<DrawingSurfaceConfig*> configs(f.filter(ws.surfConfigs)); + + // Test each config: + for (vector<DrawingSurfaceConfig*>::const_iterator + p = configs.begin(); p < configs.end(); ++p) { + Window w(ws, **p, drawingSize + 2, drawingSize + 2); + RenderingContext rc(ws, **p); + if (!ws.makeCurrent(rc, w)) + ; // XXX need to throw exception here + + // Create a result object and run the test: + Result* r = new Result(); + r->config = *p; + runOne(*r, w); + + // Save the result locally and in the results file: + results.push_back(r); + r->put(os); + } + } + catch (DrawingSurfaceFilter::Syntax e) { + env->log << "Syntax error in test's drawing-surface selection" + "criteria:\n'" << filter << "'\n"; + for (int i = 0; i < e.position; ++i) + env->log << ' '; + env->log << "^ " << e.err << '\n'; + } + catch (RenderingContext::Error) { + env->log << "Could not create a rendering context\n"; + } + + env->log << '\n'; + + // Note that we've completed the run: + hasRun = true; +} + +void +BlendFuncTest::compare(Environment& environment) { + // Save the environment for use by other member functions: + env = &environment; + + // Display the description if needed: + logDescription(); + + // Read results from previous runs: + Input1Stream is1(*this); + vector<Result*> oldR(getResults(is1)); + Input2Stream is2(*this); + vector<Result*> newR(getResults(is2)); + + // Construct a vector of surface configurations from the old run. + // (Later we'll find the best match in this vector for each config + // in the new run.) + vector<DrawingSurfaceConfig*> oldConfigs; + for (vector<Result*>::const_iterator p = oldR.begin(); p < oldR.end(); + ++p) + oldConfigs.push_back((*p)->config); + + // Compare results: + for (vector<Result*>::const_iterator newP = newR.begin(); + newP < newR.end(); ++newP) { + + // Find the drawing surface config that most closely matches + // the config for this result: + int c = (*newP)->config->match(oldConfigs); + + // If there was a match, compare the results: + if (c < 0) + env->log << name << ": NOTE no matching config for " << + (*newP)->config->conciseDescription() << '\n'; + else + compareOne(*(oldR[c]), **newP); + } + + // Get rid of the results; we don't need them for future comparisons. + for (vector<Result*>::iterator np = newR.begin(); np < newR.end(); ++np) + delete *np; + for (vector<Result*>::iterator op = oldR.begin(); op < oldR.end(); ++op) + delete *op; +} + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +BlendFuncTest::runOne(Result& r, Window& w) { + GLUtils::useScreenCoords(drawingSize + 2, drawingSize + 2); + + static GLenum srcFactors[] = { + GL_ZERO, + GL_ONE, + GL_DST_COLOR, + GL_ONE_MINUS_DST_COLOR, + GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, + GL_ONE_MINUS_DST_ALPHA, + GL_SRC_ALPHA_SATURATE + }; + static GLenum dstFactors[] = { + GL_ZERO, + GL_ONE, + GL_SRC_COLOR, + GL_ONE_MINUS_SRC_COLOR, + GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, + GL_ONE_MINUS_DST_ALPHA + }; + + bool allPassed = true; + for (unsigned int sf = 0; sf < sizeof(srcFactors)/sizeof(srcFactors[0]); + ++sf) + + for (unsigned int df = 0; + df < sizeof(dstFactors)/sizeof(dstFactors[0]); ++df) { + + Result::PartialResult p; + p.src = srcFactors[sf]; + p.dst = dstFactors[df]; + + if ((needsDstAlpha(p.src) || needsDstAlpha(p.dst)) + && (r.config->a == 0)) + continue; + + runFactorsResult res(runFactors(p.src, p.dst, + *(r.config), *env)); + w.swap(); + + p.rbErr = res.readbackErrorBits; + p.blErr = res.blendErrorBits; + r.results.push_back(p); + + if (p.rbErr > 1.0 || p.blErr > 1.0) { + env->log << name << ": FAIL " + << r.config->conciseDescription()<< '\n' + << "\tsource factor = " + << factorToName(p.src) + << ", dest factor = " + << factorToName(p.dst) + << "\n\tReadback had " << p.rbErr + << " bits in error; blending had " + << p.blErr << " bits in error.\n"; + allPassed = false; + } + } + + if (allPassed) + env->log << name << ": PASS " + << r.config->conciseDescription() << '\n'; +} // BlendFuncTest::runOne + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +BlendFuncTest::compareOne(Result& oldR, Result& newR) { + BasicStats readbackStats; + BasicStats blendStats; + + vector<Result::PartialResult>::const_iterator np; + vector<Result::PartialResult>::const_iterator op; + + for (np = newR.results.begin(); np != newR.results.end(); ++np) + // Find the matching case, if any, in the old results: + for (op = oldR.results.begin(); op != oldR.results.end(); ++op) + if (np->src == op->src && np->dst == op->dst) { + readbackStats.sample(np->rbErr - op->rbErr); + blendStats.sample(np->blErr - op->blErr); + } + + if (readbackStats.n() == static_cast<int>(newR.results.size()) + && newR.results.size() == oldR.results.size() + && readbackStats.mean() == 0.0 && blendStats.mean() == 0.0) { + if (env->options.verbosity) + env->log << name << ": SAME " + << newR.config->conciseDescription() << '\n'; + } else { + env->log << name << ": DIFF " + << newR.config->conciseDescription() << '\n'; + + if (readbackStats.mean() < 0.0) + env->log << '\t' << env->options.db2Name + << " appears to have more accurate readback.\n"; + else if (readbackStats.mean() > 0.0) + env->log << '\t' << env->options.db1Name + << " appears to have more accurate readback.\n"; + if (blendStats.mean() < 0.0) + env->log << '\t' << env->options.db2Name + << " appears to have more accurate blending.\n"; + else if (blendStats.mean() > 0.0) + env->log << '\t' << env->options.db1Name + << " appears to have more accurate blending.\n"; + if (readbackStats.n() != static_cast<int>(newR.results.size())){ + env->log << "\tThe following cases in " + << env->options.db2Name + << " have no matching test in " + << env->options.db1Name + << ":\n"; + for (np = newR.results.begin(); + np != newR.results.end(); ++np) { + for (op = oldR.results.begin(); + op != oldR.results.end(); ++op) + if (np->src == op->src + && np->dst == op->dst) + break; + if (op == oldR.results.end()) + env->log << "\t\t" + << factorToName(np->src) + << ' ' + << factorToName(np->dst) + << '\n'; + } + } + if (readbackStats.n() != static_cast<int>(oldR.results.size())){ + env->log << "\tThe following cases in " + << env->options.db1Name + << " have no matching test in " + << env->options.db2Name + << ":\n"; + for (op = oldR.results.begin(); + op != oldR.results.end(); ++op) { + for (np = newR.results.begin(); + np != newR.results.end(); ++np) + if (op->src == np->src + && op->dst == np->dst) + break; + if (np == newR.results.end()) + env->log << "\t\t" + << factorToName(op->src) + << ' ' + << factorToName(op->dst) + << '\n'; + } + } + if (env->options.verbosity) { + env->log << "\tThe following cases appear in both " + << env->options.db1Name + << " and " + << env->options.db2Name + << ":\n"; + for (np = newR.results.begin(); + np != newR.results.end(); ++np){ + for (op = oldR.results.begin(); + op != oldR.results.end(); ++op) + if (np->src == op->src + && np->dst == op->dst) + break; + if (op != oldR.results.end()) + env->log << "\t\t" + << factorToName(np->src) + << ' ' + << factorToName(np->dst) + << '\n'; + } + } + } +} // BlendFuncTest::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// logDescription: Print description on the log file, according to the +// current verbosity level. +/////////////////////////////////////////////////////////////////////////////// +void +BlendFuncTest::logDescription() { + if (env->options.verbosity) + env->log << +"----------------------------------------------------------------------\n" + << description << '\n'; +} // BlendFuncTest::logDescription + +/////////////////////////////////////////////////////////////////////////////// +// Result I/O functions: +/////////////////////////////////////////////////////////////////////////////// +void +BlendFuncTest::Result::put(ostream& s) const { + s << config->canonicalDescription() << '\n'; + + s << results.size() << '\n'; + for (vector<PartialResult>::const_iterator p = results.begin(); + p != results.end(); ++p) + s << factorToName(p->src) << ' ' + << factorToName(p->dst) << ' ' + << p->rbErr << ' ' << p->blErr << '\n'; +} // BlendFuncTest::Result::put + +bool +BlendFuncTest::Result::get(istream& s) { + SkipWhitespace(s); + string configDesc; + if (!getline(s, configDesc)) + return false; + config = new DrawingSurfaceConfig(configDesc); + + int n; + s >> n; + for (int i = 0; i < n; ++i) { + PartialResult p; + string src; + string dst; + s >> src >> dst >> p.rbErr >> p.blErr; + p.src = nameToFactor(src); + p.dst = nameToFactor(dst); + results.push_back(p); + } + + return s.good(); +} // BlendFuncTest::Result::get + +vector<BlendFuncTest::Result*> +BlendFuncTest::getResults(istream& s) { + vector<Result*> v; + while (s.good()) { + Result* r = new Result(); + if (r->get(s)) + v.push_back(r); + else { + delete r; + break; + } + } + + return v; +} // BlendFuncTest::getResults + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +BlendFuncTest blendFuncTest("blendFunc", "window, rgb", + + "This test checks all combinations of source and destination\n" + "blend factors for the GL_FUNC_ADD blend equation. It operates\n" + "on all RGB or RGBA drawing surface configurations that support\n" + "the creation of windows.\n" + "\n" + "Note that a common cause of failures for this test is small errors\n" + "introduced when an implementation scales color values incorrectly;\n" + "for example, converting an 8-bit color value to float by\n" + "dividing by 256 rather than 255, or computing a blending result\n" + "by shifting a double-width intermediate value rather than scaling\n" + "it. Also, please note that the OpenGL spec requires that when\n" + "converting from floating-point colors to integer form, the result\n" + "must be rounded to the nearest integer, not truncated.\n" + "[1.2.1, 2.13.9]\n" + "\n" + "The test reports two error measurements. The first (readback) is\n" + "the error detected when reading back raw values that were written\n" + "to the framebuffer. The error in this case should be very close\n" + "to zero, since the values are carefully constructed so that they\n" + "can be represented accurately in the framebuffer. The second\n" + "(blending) is the error detected in the result of the blending\n" + "computation. For the test to pass, these errors must both be\n" + "no greater than one least-significant bit in the framebuffer\n" + "representation of a color.\n"); + + +} // namespace GLEAN diff --git a/src/glean/tblend.h b/src/glean/tblend.h new file mode 100644 index 0000000..112643e --- /dev/null +++ b/src/glean/tblend.h @@ -0,0 +1,91 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// tblend.h: Test blending functions. + +#ifndef __tblend_h__ +#define __tblend_h__ + +#include "test.h" + +class DrawingSurfaceConfig; // Forward reference. +class GLEAN::Window; + +namespace GLEAN { + +class BlendFuncTest: public Test { + public: + BlendFuncTest(const char* testName, const char* filter, + const char* description); + virtual ~BlendFuncTest(); + + const char* filter; // Drawing surface configuration filter. + const char* description; // Verbose description of test. + + virtual void run(Environment& env); // Run test, save results. + + virtual void compare(Environment& env); + // Compare two previous runs. + + // Class for a single test result. All basic tests have a + // drawing surface configuration, plus other information + // that's specific to the test. + class Result: public Test::Result { + public: + DrawingSurfaceConfig* config; + struct PartialResult { + GLenum src; // Source blend factor. + GLenum dst; // Destination blend factor. + float rbErr; // Max readback error, in bits. + float blErr; // Max blend error, in bits. + }; + vector<PartialResult> results; + + virtual void put(ostream& s) const; + virtual bool get(istream& s); + + Result() { } + virtual ~Result() { } + }; + + vector<Result*> results; + + virtual void runOne(Result& r, GLEAN::Window& w); + virtual void compareOne(Result& oldR, Result& newR); + virtual vector<Result*> getResults(istream& s); + + void logDescription(); + +}; // class BlendFuncTest + +} // namespace GLEAN + +#endif // __tblend_h__ diff --git a/src/glean/tchgperf.cpp b/src/glean/tchgperf.cpp new file mode 100644 index 0000000..4b5e04c --- /dev/null +++ b/src/glean/tchgperf.cpp @@ -0,0 +1,451 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// tchgperf.cpp: Some basic tests of attribute-change performance. +#ifdef __UNIX__ +#include <unistd.h> +#endif + +#include <iostream> +#include <fstream> +#include <algorithm> +#include "dsconfig.h" +#include "dsfilt.h" +#include "dsurf.h" +#include "winsys.h" +#include "environ.h" +#include "rc.h" +#include "glutils.h" +#include "geomutil.h" +#include "timer.h" +#include "rand.h" +#include "image.h" +#include "tchgperf.h" +#include "misc.h" + + +namespace { + +const int drawingSize = 128; // must be power-of-2, 128 or greater +GLEAN::Image redImage(64, 64, GL_RGB, GL_UNSIGNED_BYTE, 1.0, 0.0, 0.0, 0.0); +GLuint redTex; +GLEAN::Image greenImage(64, 64, GL_RGB, GL_UNSIGNED_BYTE, 0.0, 1.0, 0.0, 0.0); +GLuint greenTex; + +int nPoints; +float* vertices; +float* texCoords; + +void +noBindDraw() { + int rowSize = 2 * nPoints; + for (int y = 0; y < nPoints - 1; ++y) { + float* t0 = texCoords + y * rowSize; + float* v0 = vertices + y * rowSize; + for (int x = 0; x < nPoints - 1; ++x) { + float* t1 = t0 + rowSize; + float* t2 = t1 + 2; + float* t3 = t0 + 2; + float* v1 = v0 + rowSize; + float* v2 = v1 + 2; + float* v3 = v0 + 2; + + glBegin(GL_TRIANGLES); + glTexCoord2fv(t0); + glVertex2fv(v0); + glTexCoord2fv(t1); + glVertex2fv(v1); + glTexCoord2fv(t2); + glVertex2fv(v2); + glEnd(); + glBegin(GL_TRIANGLES); + glTexCoord2fv(t2); + glVertex2fv(v2); + glTexCoord2fv(t3); + glVertex2fv(v3); + glTexCoord2fv(t0); + glVertex2fv(v0); + glEnd(); + + t0 += 2; + v0 += 2; + } + } +} // noBindDraw + +void +bindDraw() { + int rowSize = 2 * nPoints; + for (int y = 0; y < nPoints - 1; ++y) { + float* v0 = vertices + y * rowSize; + float* t0 = texCoords + y * rowSize; + for (int x = 0; x < nPoints - 1; ++x) { + float* t1 = t0 + rowSize; + float* t2 = t1 + 2; + float* t3 = t0 + 2; + float* v1 = v0 + rowSize; + float* v2 = v1 + 2; + float* v3 = v0 + 2; + + glBindTexture(GL_TEXTURE_2D, redTex); + glBegin(GL_TRIANGLES); + glTexCoord2fv(t0); + glVertex2fv(v0); + glTexCoord2fv(t1); + glVertex2fv(v1); + glTexCoord2fv(t2); + glVertex2fv(v2); + glEnd(); + + glBindTexture(GL_TEXTURE_2D, greenTex); + glBegin(GL_TRIANGLES); + glTexCoord2fv(t2); + glVertex2fv(v2); + glTexCoord2fv(t3); + glVertex2fv(v3); + glTexCoord2fv(t0); + glVertex2fv(v0); + glEnd(); + + t0 += 2; + v0 += 2; + } + } +} // noBindDraw + +void +logStats(GLEAN::TexBindPerf::Result& r, GLEAN::Environment* env) { + env->log << "\tApproximate texture binding time = " << r.bindTime + << " microseconds.\n\tRange of valid measurements = [" + << r.lowerBound << ", " << r.upperBound << "]\n"; +} // logStats + +} // anonymous namespace + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// Constructor/Destructor: +/////////////////////////////////////////////////////////////////////////////// +TexBindPerf::TexBindPerf(const char* aName, const char* aFilter, + const char* aDescription): + Test(aName), filter(aFilter), description(aDescription) { +} // TexBindPerf::TexBindPerf() + +TexBindPerf::~TexBindPerf() { +} // TexBindPerf::~TexBindPerf + +/////////////////////////////////////////////////////////////////////////////// +// run: run tests, save results in a vector and in the results file +/////////////////////////////////////////////////////////////////////////////// +void +TexBindPerf::run(Environment& environment) { + // Guard against multiple invocations: + if (hasRun) + return; + + // Set up environment for use by other functions: + env = &environment; + + // Document the test in the log, if requested: + logDescription(); + + // Compute results and make them available to subsequent tests: + WindowSystem& ws = env->winSys; + try { + // Open the results file: + OutputStream os(*this); + + // Select the drawing configurations for testing: + DrawingSurfaceFilter f(filter); + vector<DrawingSurfaceConfig*> configs(f.filter(ws.surfConfigs)); + + // Test each config: + for (vector<DrawingSurfaceConfig*>::const_iterator + p = configs.begin(); p < configs.end(); ++p) { + Window w(ws, **p, drawingSize + 2, drawingSize + 2); + RenderingContext rc(ws, **p); + if (!ws.makeCurrent(rc, w)) + ; // XXX need to throw exception here + + // Create a result object and run the test: + Result* r = new Result(); + r->config = *p; + runOne(*r, w); + + // Save the result locally and in the results file: + results.push_back(r); + r->put(os); + } + } + catch (DrawingSurfaceFilter::Syntax e) { + env->log << "Syntax error in test's drawing-surface selection" + "criteria:\n'" << filter << "'\n"; + for (int i = 0; i < e.position; ++i) + env->log << ' '; + env->log << "^ " << e.err << '\n'; + } + catch (RenderingContext::Error) { + env->log << "Could not create a rendering context\n"; + } + + env->log << '\n'; + + // Note that we've completed the run: + hasRun = true; +} + +void +TexBindPerf::compare(Environment& environment) { + // Save the environment for use by other member functions: + env = &environment; + + // Display the description if needed: + logDescription(); + + // Read results from previous runs: + Input1Stream is1(*this); + vector<Result*> oldR(getResults(is1)); + Input2Stream is2(*this); + vector<Result*> newR(getResults(is2)); + + // Construct a vector of surface configurations from the old run. + // (Later we'll find the best match in this vector for each config + // in the new run.) + vector<DrawingSurfaceConfig*> oldConfigs; + for (vector<Result*>::const_iterator p = oldR.begin(); p < oldR.end(); + ++p) + oldConfigs.push_back((*p)->config); + + // Compare results: + for (vector<Result*>::const_iterator newP = newR.begin(); + newP < newR.end(); ++newP) { + + // Find the drawing surface config that most closely matches + // the config for this result: + int c = (*newP)->config->match(oldConfigs); + + // If there was a match, compare the results: + if (c < 0) + env->log << name << ": NOTE no matching config for " << + (*newP)->config->conciseDescription() << '\n'; + else + compareOne(*(oldR[c]), **newP); + } + + // Get rid of the results; we don't need them for future comparisons. + for (vector<Result*>::iterator np = newR.begin(); np < newR.end(); ++np) + delete *np; + for (vector<Result*>::iterator op = oldR.begin(); op < oldR.end(); ++op) + delete *op; +} + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// + +typedef void (*TIME_FUNC) (); + +void +TexBindPerf::runOne(Result& r, Window& w) { + Timer time; + time.calibrate((TIME_FUNC)glFinish, (TIME_FUNC) glFinish); + + glGenTextures(1, &redTex); + glBindTexture(GL_TEXTURE_2D, redTex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + redImage.makeMipmaps(GL_RGB); + + glGenTextures(1, &greenTex); + glBindTexture(GL_TEXTURE_2D, greenTex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + greenImage.makeMipmaps(GL_RGB); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); + + GLUtils::useScreenCoords(drawingSize + 2, drawingSize + 2); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glEnable(GL_TEXTURE_2D); + glColor4f(1.0, 1.0, 1.0, 1.0); + + nPoints = drawingSize / 2; // Yields 1-pixel triangles. + + RandomDouble vRand(142857); + RandomMesh2D v(1.0, drawingSize, nPoints, 1.0, drawingSize, nPoints, + vRand); + vertices = v(0, 0); + + RandomDouble tRand(314159); + RandomMesh2D t(0.0, 1.0, nPoints, 0.0, 1.0, nPoints, tRand); + texCoords = t(0, 0); + + int nTris = (nPoints - 1) * (nPoints - 1) / 2; + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + vector<float> measurements; + for (int i = 0; i < 5; ++i) { + env->quiesce(); + double tBind = time.time((TIME_FUNC)glFinish, (TIME_FUNC) bindDraw, (TIME_FUNC) glFinish); + w.swap(); // So the user can see something happening. + + env->quiesce(); + double tNoBind = time.time((TIME_FUNC)glFinish, (TIME_FUNC)noBindDraw, (TIME_FUNC)glFinish); + w.swap(); + + double bindTime = 1E6 * (tBind - tNoBind) / nTris; + if (bindTime < 0.0) { + // This can happen if the system isn't quiescent; + // some process sneaks in and takes wall-clock time + // when ``noBindDraw'' is running. Just flush it + // and try again. (Note: You really shouldn't be + // running timing tests on a system where other + // processes are active!) + --i; + continue; + } + + measurements.push_back(bindTime); + } + + sort(measurements.begin(), measurements.end()); + r.bindTime = measurements[2]; + r.lowerBound = measurements[1]; + r.upperBound = measurements[3]; + + env->log << name << ": PASS " + << r.config->conciseDescription() << '\n'; + logStats(r, env); +} // TexBindPerf::runOne + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +TexBindPerf::compareOne(Result& oldR, Result& newR) { + if (oldR.lowerBound < newR.bindTime && newR.bindTime < oldR.upperBound + && newR.lowerBound < oldR.bindTime && oldR.bindTime < newR.upperBound){ + if (env->options.verbosity) + env->log << name << ": SAME " + << newR.config->conciseDescription() + << "\n\tEach test time falls within the " + "valid measurement range of the\n" + "\tother test time.\n"; + } else { + env->log << name << ": DIFF " + << newR.config->conciseDescription() << '\n'; + env->log << '\t' + << ((oldR.bindTime < newR.bindTime)? + env->options.db1Name: env->options.db2Name) + << " appears to have higher performance.\n"; + } + if (env->options.verbosity) { + env->log << env->options.db1Name << ':'; + logStats(oldR, env); + env->log << env->options.db2Name << ':'; + logStats(newR, env); + } +} // TexBindPerf::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// logDescription: Print description on the log file, according to the +// current verbosity level. +/////////////////////////////////////////////////////////////////////////////// +void +TexBindPerf::logDescription() { + if (env->options.verbosity) + env->log << +"----------------------------------------------------------------------\n" + << description << '\n'; +} // TexBindPerf::logDescription + +/////////////////////////////////////////////////////////////////////////////// +// Result I/O functions: +/////////////////////////////////////////////////////////////////////////////// +void +TexBindPerf::Result::put(ostream& s) const { + s << config->canonicalDescription() << '\n'; + + s << bindTime << ' ' << lowerBound << ' ' << upperBound << '\n'; +} // TexBindPerf::Result::put + +bool +TexBindPerf::Result::get(istream& s) { + SkipWhitespace(s); + string configDesc; + if (!getline(s, configDesc)) + return false; + config = new DrawingSurfaceConfig(configDesc); + + s >> bindTime >> lowerBound >> upperBound; + return s.good(); +} // TexBindPerf::Result::get + +vector<TexBindPerf::Result*> +TexBindPerf::getResults(istream& s) { + vector<Result*> v; + while (s.good()) { + Result* r = new Result(); + if (r->get(s)) + v.push_back(r); + else { + delete r; + break; + } + } + + return v; +} // TexBindPerf::getResults + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +TexBindPerf texBindPerfTest("texBindPerf", "window, rgb, z", + + "This test makes a rough estimate of the cost of a glBindTexture()\n" + "operation, expressed in microseconds.\n" + "\n" + "Since the apparent cost of a texture bind is dependent on many\n" + "factors (including the fraction of the texture map that's actually\n" + "used for drawing, on machines that cache textures; texture map\n" + "size; texel format; etc.), a general-purpose test can only estimate\n" + "it. In this test we do so by drawing random triangles of very\n" + "small size, and reporting simple statistics concerning the cost.\n"); + + +} // namespace GLEAN diff --git a/src/glean/tchgperf.h b/src/glean/tchgperf.h new file mode 100644 index 0000000..902ecb0 --- /dev/null +++ b/src/glean/tchgperf.h @@ -0,0 +1,87 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// tchgperf.h: Some basic tests of attribute-change performance. + +#ifndef __tchgperf_h__ +#define __tchgperf_h__ + +#include "test.h" + +class DrawingSurfaceConfig; // Forward reference. +class GLEAN::Window; + +namespace GLEAN { + +class TexBindPerf: public Test { + public: + TexBindPerf(const char* testName, const char* filter, + const char* description); + virtual ~TexBindPerf(); + + const char* filter; // Drawing surface configuration filter. + const char* description; // Verbose description of test. + + virtual void run(Environment& env); // Run test, save results. + + virtual void compare(Environment& env); + // Compare two previous runs. + + // Class for a single test result. All basic tests have a + // drawing surface configuration, plus other information + // that's specific to the test. + class Result: public Test::Result { + public: + DrawingSurfaceConfig* config; + float bindTime; + float lowerBound; + float upperBound; + + virtual void put(ostream& s) const; + virtual bool get(istream& s); + + Result() { } + virtual ~Result() { } + }; + + vector<Result*> results; + + virtual void runOne(Result& r, GLEAN::Window& w); + virtual void compareOne(Result& oldR, Result& newR); + virtual vector<Result*> getResults(istream& s); + + void logDescription(); + +}; // class TexBindPerf + +} // namespace GLEAN + +#endif // __tchgperf_h__ diff --git a/src/glean/test.cpp b/src/glean/test.cpp new file mode 100644 index 0000000..f9ff8cd --- /dev/null +++ b/src/glean/test.cpp @@ -0,0 +1,124 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// test.cpp: implementation of base class for tests +#ifdef __UNIX__ +#include <unistd.h> +#endif + +#include <iostream> +#include "dsconfig.h" +#include "dsfilt.h" +#include "dsurf.h" +#include "winsys.h" +#include "environ.h" +#include "rc.h" +#include "test.h" + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// Class variables for automatic construction of list of all tests +/////////////////////////////////////////////////////////////////////////////// +Test* Test::testList; // Guaranteed initialized to zero at startup, + // before any constructors are invoked. + // (See discussion in section 10.4.9, + // page 252, of ``C++ Programming Language'' + // (third edition).) + +int Test::testCount; // Also initialized to zero. + +/////////////////////////////////////////////////////////////////////////////// +// Constructor/Destructor: +/////////////////////////////////////////////////////////////////////////////// +Test::Test(const char* testName): + name(testName) { + hasRun = false; + nextTest = testList; + testList = this; + ++testCount; +} // Test::Test() + +Test::~Test() { +} // Test::~Test + +/////////////////////////////////////////////////////////////////////////////// +// Stream opening utilities for results databases +/////////////////////////////////////////////////////////////////////////////// + +Test::OutputStream::OutputStream(Test& t) { + s = new ofstream(t.env->resultFileName(t.name).c_str()); + if (!*s) + throw Test::CantOpenResultsFile(t.name, t.env->options.db1Name); +} // Test::OutputStream::OutputStream + +Test::OutputStream::~OutputStream() { + s->close(); + delete s; +} // Test::OutputStream::~OutputStream + +Test::OutputStream::operator ofstream& () { + return *s; +} // Test::OutputStream::operator ::ofstream& + +Test::Input1Stream::Input1Stream(Test& t) { + s = new ifstream(t.env->resultFileName( + t.env->options.db1Name, t.name).c_str()); + if (!*s) + throw Test::CantOpenResultsFile(t.name, t.env->options.db1Name); +} // Test::Input1Stream::Input1Stream + +Test::Input1Stream::~Input1Stream() { + s->close(); + delete s; +} // Test::Input1Stream::~Input1Stream + +Test::Input1Stream::operator ifstream& () { + return *s; +} // Test::Input1Stream::operator ::ifstream& + +Test::Input2Stream::Input2Stream(Test& t) { + s = new ifstream(t.env->resultFileName( + t.env->options.db2Name, t.name).c_str()); + if (!*s) + throw Test::CantOpenResultsFile(t.name, t.env->options.db2Name); +} // Test::Input2Stream::Input2Stream + +Test::Input2Stream::~Input2Stream() { + s->close(); + delete s; +} // Test::Input2Stream::~Input2Stream + +Test::Input2Stream::operator ifstream& () { + return *s; +} // Test::Input2Stream::operator ::ifstream& + +} // namespace GLEAN diff --git a/src/glean/test.h b/src/glean/test.h new file mode 100644 index 0000000..88ea23d --- /dev/null +++ b/src/glean/test.h @@ -0,0 +1,140 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// test.h: Base class for all tests + +// This class encapsulates base functionality used by all tests. Some +// of this is fairly trivial (the test name, for example). One of the +// most important nontrivial functions is the use of the constructor +// to build a linked list of test objects; this eliminates the need to +// maintain a separate table of tests. This class also provides a +// flag for determining if a test has been run, which allows tests to +// invoke one another and make use of previous results without forcing +// tests to run multiple times. Finally, it provides a basic +// framework for recording a vector of results (which typically will +// vary depending on the drawing surface configuration or the +// particular type of drawing surface used). + + + +#ifndef __test_h__ +#define __test_h__ + +#include <string> +#include <vector> +#include <fstream> + +using namespace std; + +namespace GLEAN { + +class Environment; // Mutually-recursive and forward references. +class DrawingSurfaceConfig; + +class Test { + public: + Test(const char* testName); + virtual ~Test(); + + string name; // Test name. Should avoid characters + // that aren't universally available in + // filenames, since it might be used to + // construct such names. + + bool hasRun; // True if test has been run. + + Environment* env; // Environment in which runs or comparisons + // will be performed. + + virtual void run(Environment& env) = 0; // Run test, save results. + + virtual void compare(Environment& env) = 0; + // Compare two previous runs. + + // Base class for a single test result. A test may have many results + // (for example, one per drawing surface configuration), so in general + // individual tests will have a vector of these objects. + class Result { + public: + virtual void put(ostream& s) const = 0; + virtual bool get(istream& s) = 0; + Result() { } + virtual ~Result() { } + }; + + + // Exceptions: + struct Error { }; // Base class for all exceptions. + struct CantOpenResultsFile: public Error { + const string& testName; + const string& dbName; + CantOpenResultsFile(const string& test, const string& db): + testName(test), dbName(db) { } + }; + + + // OutputStream and Input*Stream objects provide convenient access + // to the results database, and close the file streams automatically + // when their destructors are executed. + class OutputStream { // Open an output stream for storing results. + ofstream* s; + public: + OutputStream(Test& t); + ~OutputStream(); + operator ofstream& (); + }; + class Input1Stream { // Open db #1 input stream for reading results. + ifstream* s; + public: + Input1Stream(Test& t); + ~Input1Stream(); + operator ifstream& (); + }; + class Input2Stream { // Open db #2 input stream for reading results. + ifstream* s; + public: + Input2Stream(Test& t); + ~Input2Stream(); + operator ifstream& (); + }; + + + static Test* testList; // List of all test objects. Built by + // constructor Test::Test(...). + + Test* nextTest; // Link to next test object. + + static int testCount; // Count of elements in testList. +}; // class Test + +} // namespace GLEAN + +#endif // __test_h__ diff --git a/src/glean/tgetstr.cpp b/src/glean/tgetstr.cpp new file mode 100644 index 0000000..e970d1b --- /dev/null +++ b/src/glean/tgetstr.cpp @@ -0,0 +1,344 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// tgetstr.cpp: implementation of OpenGL glGetString() tests +#ifdef __UNIX__ +#include <unistd.h> +#endif + +#include <iostream> +#include <fstream> +#include <vector> +#include <algorithm> +#include "dsconfig.h" +#include "dsfilt.h" +#include "dsurf.h" +#include "winsys.h" +#include "environ.h" +#include "rc.h" +#include "lex.h" +#include "glutils.h" +#include "tgetstr.h" +#include "misc.h" + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// Constructor/Destructor: +/////////////////////////////////////////////////////////////////////////////// +GetStringTest::GetStringTest(const char* aName, const char* aFilter, + const char* aDescription): + Test(aName), filter(aFilter), description(aDescription) { +} // GetStringTest::GetStringTest() + +GetStringTest::~GetStringTest() { +} // GetStringTest::~GetStringTest + +/////////////////////////////////////////////////////////////////////////////// +// run: run tests, save results in a vector and in the results file +/////////////////////////////////////////////////////////////////////////////// +void +GetStringTest::run(Environment& environment) { + // Guard against multiple invocations: + if (hasRun) + return; + + // Set up environment for use by other functions: + env = &environment; + + // Document the test in the log, if requested: + logDescription(); + + // Compute results and make them available to subsequent tests: + WindowSystem& ws = env->winSys; + try { + // Open the results file: + OutputStream os(*this); + + // Select the drawing configurations for testing: + DrawingSurfaceFilter f(filter); + vector<DrawingSurfaceConfig*> configs(f.filter(ws.surfConfigs)); + + // Test each config: + for (vector<DrawingSurfaceConfig*>::const_iterator + p = configs.begin(); p < configs.end(); ++p) { + Window w(ws, **p, 258, 258); + RenderingContext rc(ws, **p); + if (!ws.makeCurrent(rc, w)) + ; // XXX need to throw exception here + + // Create a result object and run the test: + Result* r = new Result(); + r->config = *p; + runOne(*r); + + // Save the result locally and in the results file: + results.push_back(r); + r->put(os); + } + } + catch (DrawingSurfaceFilter::Syntax e) { + env->log << "Syntax error in test's drawing-surface selection" + "criteria:\n'" << filter << "'\n"; + for (int i = 0; i < e.position; ++i) + env->log << ' '; + env->log << "^ " << e.err << '\n'; + } + catch (RenderingContext::Error) { + env->log << "Could not create a rendering context\n"; + } + + env->log << '\n'; + + // Note that we've completed the run: + hasRun = true; +} + +void +GetStringTest::compare(Environment& environment) { + // Save the environment for use by other member functions: + env = &environment; + + // Display the description if needed: + logDescription(); + + // Read results from previous runs: + Input1Stream is1(*this); + vector<Result*> oldR(getResults(is1)); + Input2Stream is2(*this); + vector<Result*> newR(getResults(is2)); + + // Construct a vector of surface configurations from the old run. + // (Later we'll find the best match in this vector for each config + // in the new run.) + vector<DrawingSurfaceConfig*> oldConfigs; + for (vector<Result*>::const_iterator p = oldR.begin(); p < oldR.end(); + ++p) + oldConfigs.push_back((*p)->config); + + // Compare results: + for (vector<Result*>::const_iterator newP = newR.begin(); + newP < newR.end(); ++newP) { + + // Find the drawing surface config that most closely matches + // the config for this result: + int c = (*newP)->config->match(oldConfigs); + + // If there was a match, compare the results: + if (c < 0) + env->log << name << ": NOTE no matching config for " << + (*newP)->config->conciseDescription() << '\n'; + else + compareOne(*(oldR[c]), **newP); + } + + // Get rid of the results; we don't need them for future comparisons. + for (vector<Result*>::iterator np = newR.begin(); np < newR.end(); ++np) + delete *np; + for (vector<Result*>::iterator op = oldR.begin(); op < oldR.end(); ++op) + delete *op; +} + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +GetStringTest::runOne(Result& r) { + r.vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR)); + r.renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER)); + r.version = reinterpret_cast<const char*>(glGetString(GL_VERSION)); + r.extensions = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)); + env->log << name << ": PASS " << r.config->conciseDescription()<<'\n'; + if (env->options.verbosity) { + env->log << "\tvendor: " << r.vendor << '\n'; + env->log << "\trenderer: " << r.renderer << '\n'; + env->log << "\tversion: " << r.version << '\n'; + env->log << "\textensions: " << r.extensions << '\n'; + } +} // GetStringTest::runOne + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +GetStringTest::compareOne(Result& oldR, Result& newR) { + if (oldR.vendor == newR.vendor && oldR.renderer == newR.renderer + && oldR.version == newR.version && oldR.extensions == newR.extensions){ + if (env->options.verbosity) + env->log << name << ": SAME " << + newR.config->conciseDescription() << '\n'; + } else { + env->log << name << ": DIFF " + << newR.config->conciseDescription() << '\n'; + if (oldR.vendor != newR.vendor) { + env->log << '\t' << env->options.db1Name + << " vendor: " << oldR.vendor; + env->log << '\t' << env->options.db2Name + << " vendor: " << newR.vendor; + } + if (oldR.renderer != newR.renderer) { + env->log << '\t' << env->options.db1Name + << " renderer: " << oldR.renderer; + env->log << '\t' << env->options.db2Name + << " renderer: " << newR.renderer; + } + if (oldR.version != newR.version) { + env->log << '\t' << env->options.db1Name + << " version: " << oldR.version; + env->log << '\t' << env->options.db2Name + << " version: " << newR.version; + } + if (oldR.extensions != newR.extensions) { + vector<string> oldExts; + Lex oldLex(oldR.extensions.c_str()); + for (;;) { + oldLex.next(); + if (oldLex.token == Lex::ID) + oldExts.push_back(oldLex.id); + else + break; + } + sort(oldExts.begin(), oldExts.end()); + + vector<string> newExts; + Lex newLex(newR.extensions.c_str()); + for (;;) { + newLex.next(); + if (newLex.token == Lex::ID) + newExts.push_back(newLex.id); + else + break; + } + sort(newExts.begin(), newExts.end()); + + vector<string> d(max(oldExts.size(), newExts.size())); + vector<string>::iterator dEnd; + + dEnd = set_difference(oldExts.begin(), oldExts.end(), + newExts.begin(), newExts.end(), d.begin()); + if (dEnd != d.begin()) { + env->log << "\tExtensions in " << + env->options.db1Name << " but not in " + << env->options.db2Name << ":\n"; + for (vector<string>::iterator p = d.begin(); + p != dEnd; ++p) + env->log << "\t\t" << *p << '\n'; + } + + dEnd = set_difference(newExts.begin(), newExts.end(), + oldExts.begin(), oldExts.end(), d.begin()); + if (dEnd != d.begin()) { + env->log << "\tExtensions in " << + env->options.db2Name << " but not in " + << env->options.db1Name << ":\n"; + for (vector<string>::iterator p = d.begin(); + p != dEnd; ++p) + env->log << "\t\t" << *p << '\n'; + } + + dEnd = set_intersection(newExts.begin(), newExts.end(), + oldExts.begin(), oldExts.end(), d.begin()); + if (dEnd != d.begin()) { + env->log << "\tExtensions in both " << + env->options.db2Name << " and in " + << env->options.db1Name << ":\n"; + for (vector<string>::iterator p = d.begin(); + p != dEnd; ++p) + env->log << "\t\t" << *p << '\n'; + } + } + } +} // GetStringTest::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// logDescription: Print description on the log file, according to the +// current verbosity level. +/////////////////////////////////////////////////////////////////////////////// +void +GetStringTest::logDescription() { + if (env->options.verbosity) + env->log << +"----------------------------------------------------------------------\n" + << description << '\n'; +} // GetStringTest::logDescription + +/////////////////////////////////////////////////////////////////////////////// +// Result I/O functions: +/////////////////////////////////////////////////////////////////////////////// +void +GetStringTest::Result::put(ostream& s) const { + s << config->canonicalDescription() << '\n' + << vendor << '\n' + << renderer << '\n' + << version << '\n' + << extensions << '\n'; +} // GetStringTest::Result::put + +bool +GetStringTest::Result::get(istream& s) { + SkipWhitespace(s); + string configDesc; + if (!getline(s, configDesc)) + return false; + config = new DrawingSurfaceConfig(configDesc); + getline(s, vendor); + getline(s, renderer); + getline(s, version); + getline(s, extensions); + return s.good(); +} // GetStringTest::Result::get + +vector<GetStringTest::Result*> +GetStringTest::getResults(istream& s) { + vector<Result*> v; + while (s.good()) { + Result* r = new Result(); + if (r->get(s)) + v.push_back(r); + else { + delete r; + break; + } + } + + return v; +} // GetStringTest::getResults + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +GetStringTest getStringTest("getString", "window", + "This test checks the contents of the strings returned by\n" + "glGetString(): the vendor name, renderer name, version, and\n" + "extensions. It is run on every OpenGL-capable drawing surface\n" + "configuration that supports creation of a window.\n"); + +} // namespace GLEAN diff --git a/src/glean/tgetstr.h b/src/glean/tgetstr.h new file mode 100644 index 0000000..e611cf9 --- /dev/null +++ b/src/glean/tgetstr.h @@ -0,0 +1,87 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// tgetstr.h: Check OpenGL vendor, renderer, version, and extension strings + +// See tbasic.cpp for the basic test structure. + + +#ifndef __tgetstr_h__ +#define __tgetstr_h__ + +#include "test.h" + +class DrawingSurfaceConfig; // Forward reference. + +namespace GLEAN { + +class GetStringTest: public Test { + public: + GetStringTest(const char* testName, const char* filter, + const char* description); + virtual ~GetStringTest(); + + const char* filter; // Drawing surface configuration filter. + const char* description; // Verbose description of test. + + virtual void run(Environment& env); // Run test, save results. + + virtual void compare(Environment& env); + // Compare two previous runs. + + class Result: public Test::Result { + public: + DrawingSurfaceConfig* config; + string vendor; + string renderer; + string version; + string extensions; + + virtual void put(ostream& s) const; + virtual bool get(istream& s); + + Result() { } + virtual ~Result() { } + }; + + vector<Result*> results; + + virtual void runOne(Result& r); + virtual void compareOne(Result& oldR, Result& newR); + virtual vector<Result*> getResults(istream& s); + + void logDescription(); + +}; // class GetStringTest + +} // namespace GLEAN + +#endif // __tgetstr_h__ diff --git a/src/glean/trgbtris.cpp b/src/glean/trgbtris.cpp new file mode 100644 index 0000000..142224c --- /dev/null +++ b/src/glean/trgbtris.cpp @@ -0,0 +1,354 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// trgbtris.cpp: example image-based test to show use of TIFF images +#if defined __UNIX__ +#include <unistd.h> +#endif + +#include <iostream> +#include <fstream> +#include <algorithm> +#include "dsconfig.h" +#include "dsfilt.h" +#include "dsurf.h" +#include "winsys.h" +#include "environ.h" +#include "rc.h" +#include "glutils.h" +#include "geomutil.h" +#include "rand.h" +#include "stats.h" +#include "image.h" +#include "trgbtris.h" +#include "misc.h" + +namespace { + +const int drawingSize = 64; // Don't make this too large! + // Uncompressed TIFF images demand a lot of + // disk space and network bandwidth. + +void +logStats(GLEAN::BasicStats& stats, GLEAN::Environment* env) { + env->log << "\t\tmin = " << stats.min() << ", max = " << stats.max() + << "\n\t\tmean = " << stats.mean() << ", standard deviation = " + << stats.deviation() << '\n'; +} + +} // anonymous namespace + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// Constructor/Destructor: +/////////////////////////////////////////////////////////////////////////////// +RGBTriStripTest::RGBTriStripTest(const char* aName, const char* aFilter, + const char* aDescription): + Test(aName), filter(aFilter), description(aDescription) { +} // RGBTriStripTest::RGBTriStripTest() + +RGBTriStripTest::~RGBTriStripTest() { +} // RGBTriStripTest::~RGBTriStripTest + +/////////////////////////////////////////////////////////////////////////////// +// run: run tests, save results in a vector and in the results file +/////////////////////////////////////////////////////////////////////////////// +void +RGBTriStripTest::run(Environment& environment) { + // Guard against multiple invocations: + if (hasRun) + return; + + // Set up environment for use by other functions: + env = &environment; + + // Document the test in the log, if requested: + logDescription(); + + // Compute results and make them available to subsequent tests: + WindowSystem& ws = env->winSys; + try { + // Open the results file: + OutputStream os(*this); + + // Select the drawing configurations for testing: + DrawingSurfaceFilter f(filter); + vector<DrawingSurfaceConfig*> configs(f.filter(ws.surfConfigs)); + + // Test each config: + for (vector<DrawingSurfaceConfig*>::const_iterator + p = configs.begin(); p < configs.end(); ++p) { + Window w(ws, **p, drawingSize + 2, drawingSize + 2); + RenderingContext rc(ws, **p); + if (!ws.makeCurrent(rc, w)) + ; // XXX need to throw exception here + + // Create a result object and run the test: + Result* r = new Result(); + r->config = *p; + r->imageNumber = 1 + (p - configs.begin()); + runOne(*r, w); + + // Save the result locally and in the results file: + results.push_back(r); + r->put(os); + } + } + catch (DrawingSurfaceFilter::Syntax e) { + env->log << "Syntax error in test's drawing-surface selection" + "criteria:\n'" << filter << "'\n"; + for (int i = 0; i < e.position; ++i) + env->log << ' '; + env->log << "^ " << e.err << '\n'; + } + catch (RenderingContext::Error) { + env->log << "Could not create a rendering context\n"; + } + + env->log << '\n'; + + // Note that we've completed the run: + hasRun = true; +} + +void +RGBTriStripTest::compare(Environment& environment) { + // Save the environment for use by other member functions: + env = &environment; + + // Display the description if needed: + logDescription(); + + // Read results from previous runs: + Input1Stream is1(*this); + vector<Result*> oldR(getResults(is1)); + Input2Stream is2(*this); + vector<Result*> newR(getResults(is2)); + + // Construct a vector of surface configurations from the old run. + // (Later we'll find the best match in this vector for each config + // in the new run.) + vector<DrawingSurfaceConfig*> oldConfigs; + for (vector<Result*>::const_iterator p = oldR.begin(); p < oldR.end(); + ++p) + oldConfigs.push_back((*p)->config); + + // Compare results: + for (vector<Result*>::const_iterator newP = newR.begin(); + newP < newR.end(); ++newP) { + + // Find the drawing surface config that most closely matches + // the config for this result: + int c = (*newP)->config->match(oldConfigs); + + // If there was a match, compare the results: + if (c < 0) + env->log << name << ": NOTE no matching config for " << + (*newP)->config->conciseDescription() << '\n'; + else + compareOne(*(oldR[c]), **newP); + } + + // Get rid of the results; we don't need them for future comparisons. + for (vector<Result*>::iterator np = newR.begin(); np < newR.end(); ++np) + delete *np; + for (vector<Result*>::iterator op = oldR.begin(); op < oldR.end(); ++op) + delete *op; +} + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +RGBTriStripTest::runOne(Result& r, Window& w) { + GLUtils::useScreenCoords(drawingSize + 2, drawingSize + 2); + + int nPoints = 20; // Exact value doesn't really matter. + RandomDouble vRand(142857); + RandomMesh2D v(1.0, drawingSize, nPoints, 1.0, drawingSize, nPoints, + vRand); + + RandomDouble cRand(271828); + + glClear(GL_COLOR_BUFFER_BIT); + glShadeModel(GL_SMOOTH); + + for (int row = 0; row < nPoints - 1; ++row) { + glBegin(GL_TRIANGLE_STRIP); + for (int col = 0; col < nPoints; ++col) { + float r = cRand.next(); + float g = cRand.next(); + float b = cRand.next(); + glColor3f(r, g, b); + glVertex2fv(v(row, col)); + r = cRand.next(); + g = cRand.next(); + b = cRand.next(); + glColor3f(r, g, b); + glVertex2fv(v(row + 1, col)); + } + glEnd(); + } + w.swap(); + + Image image(drawingSize + 2, drawingSize + 2, GL_RGB, GL_FLOAT); + image.read(0, 0); // Invoke glReadPixels to read the image. + image.writeTIFF(env->imageFileName(name, r.imageNumber)); + + env->log << name << ": NOTE " + << r.config->conciseDescription() << '\n' + << "\tImage number " << r.imageNumber << '\n'; + if (env->options.verbosity) + env->log << + "\tThis test does not check its result. Please view\n" + "\tthe image to verify that the result is correct, or\n" + "\tcompare it to a known-good result from a different\n" + "\trun of glean.\n"; +} // RGBTriStripTest::runOne + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +RGBTriStripTest::compareOne(Result& oldR, Result& newR) { + // Fetch the old and new images: + Image oldI; + oldI.readTIFF(env->image1FileName(name, oldR.imageNumber)); + Image newI; + newI.readTIFF(env->image2FileName(name, newR.imageNumber)); + + // Register the images, and gather statistics about the differences + // for each color channel: + Image::Registration reg(oldI.reg(newI)); + + // Compute worst-case tolerance (1 LSB in the shallowest drawing + // surface configuration) for each color channel: + double rTol = 1.0 / (1 << min(oldR.config->r, newR.config->r)); + double gTol = 1.0 / (1 << min(oldR.config->g, newR.config->g)); + double bTol = 1.0 / (1 << min(oldR.config->b, newR.config->b)); + + // We'll conclude that the images are the ``same'' if the maximum + // absolute error is no more than 1 LSB (in the shallowest config): + if (reg.stats[0].max() <= rTol && reg.stats[1].max() <= gTol + && reg.stats[2].max() <= bTol) { + if (env->options.verbosity) { + env->log << name << ": SAME " + << newR.config->conciseDescription() << '\n'; + if (reg.stats[0].max() == 0 && reg.stats[1].max() == 0 + && reg.stats[1].max() == 0) + env->log << "\tImages are exactly equal\n"; + else + env->log << "\tImages are approximately equal\n"; + } + } else { + env->log << name << ": DIFF " + << newR.config->conciseDescription() << '\n' + << "\tDifference exceeds 1 LSB in at least one " + "color channel\n"; + } + if (env->options.verbosity) { + env->log << "\tred:\n"; + logStats(reg.stats[0], env); + env->log << "\tgreen:\n"; + logStats(reg.stats[1], env); + env->log << "\tblue:\n"; + logStats(reg.stats[2], env); + } +} // RGBTriStripTest::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// logDescription: Print description on the log file, according to the +// current verbosity level. +/////////////////////////////////////////////////////////////////////////////// +void +RGBTriStripTest::logDescription() { + if (env->options.verbosity) + env->log << +"----------------------------------------------------------------------\n" + << description << '\n'; +} // RGBTriStripTest::logDescription + +/////////////////////////////////////////////////////////////////////////////// +// Result I/O functions: +/////////////////////////////////////////////////////////////////////////////// +void +RGBTriStripTest::Result::put(ostream& s) const { + s << config->canonicalDescription() << '\n'; + + s << imageNumber << '\n'; +} // RGBTriStripTest::Result::put + +bool +RGBTriStripTest::Result::get(istream& s) { + SkipWhitespace(s); + string configDesc; + if (!getline(s, configDesc)) + return false; + config = new DrawingSurfaceConfig(configDesc); + + s >> imageNumber; + return s.good(); +} // RGBTriStripTest::Result::get + +vector<RGBTriStripTest::Result*> +RGBTriStripTest::getResults(istream& s) { + vector<Result*> v; + while (s.good()) { + Result* r = new Result(); + if (r->get(s)) + v.push_back(r); + else { + delete r; + break; + } + } + + return v; +} // RGBTriStripTest::getResults + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +RGBTriStripTest rgbTriStripTest("rgbTriStrip", "window, rgb", + + "The best approach when designing a test is to make it\n" + "self-checking; that is, the test itself should determine\n" + "whether the image or other data structure that it produces is\n" + "correct. However, some tests are difficult to design in this\n" + "way, and for some other tests (like stress tests or regression\n" + "tests concerning previously-reported bugs) it may be\n" + "unnecessary. For such tests, glean provides mechanisms to\n" + "save images and compare them to images generated from other\n" + "runs. This test simply exercises those mechanisms.\n"); + + +} // namespace GLEAN diff --git a/src/glean/trgbtris.h b/src/glean/trgbtris.h new file mode 100644 index 0000000..d7368d0 --- /dev/null +++ b/src/glean/trgbtris.h @@ -0,0 +1,85 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// trgbtris.h: example image-based test to show use of TIFF images + +#ifndef __trgbtris_h__ +#define __trgbtris_h__ + +#include "test.h" + +class DrawingSurfaceConfig; // Forward reference. +class GLEAN::Window; + +namespace GLEAN { + +class RGBTriStripTest: public Test { + public: + RGBTriStripTest(const char* testName, const char* filter, + const char* description); + virtual ~RGBTriStripTest(); + + const char* filter; // Drawing surface configuration filter. + const char* description; // Verbose description of test. + + virtual void run(Environment& env); // Run test, save results. + + virtual void compare(Environment& env); + // Compare two previous runs. + + // Class for a single test result. All basic tests have a + // drawing surface configuration, plus other information + // that's specific to the test. + class Result: public Test::Result { + public: + DrawingSurfaceConfig* config; + int imageNumber; + + virtual void put(ostream& s) const; + virtual bool get(istream& s); + + Result() { } + virtual ~Result() { } + }; + + vector<Result*> results; + + virtual void runOne(Result& r, GLEAN::Window& w); + virtual void compareOne(Result& oldR, Result& newR); + virtual vector<Result*> getResults(istream& s); + + void logDescription(); + +}; // class RGBTriStripTest + +} // namespace GLEAN + +#endif // __trgbtris_h__ diff --git a/src/glean/version.h b/src/glean/version.h new file mode 100644 index 0000000..5204192 --- /dev/null +++ b/src/glean/version.h @@ -0,0 +1,47 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// GLEAN version information + + +#ifndef __version_h__ +#define __version_h__ + +#define GLEAN_MAJOR_VERSION 1 +#define GLEAN_MINOR_VERSION 0 + +namespace GLEAN { + +const char* versionString = "glean v1.0 8 November 1999"; + +} // namespace GLEAN + +#endif // __version_h__ diff --git a/src/glean/winsys.cpp b/src/glean/winsys.cpp new file mode 100644 index 0000000..b9c3cbe --- /dev/null +++ b/src/glean/winsys.cpp @@ -0,0 +1,181 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// winsys.cpp: implementation of window-system services class + +#include <iostream> +#include "options.h" +#include "winsys.h" +#include "dsconfig.h" +#include "dsfilt.h" +#include "dsurf.h" +#include "rc.h" + +namespace GLEAN { + + +/////////////////////////////////////////////////////////////////////////////// +// Constructors +/////////////////////////////////////////////////////////////////////////////// +#if defined(__X11__) +WindowSystem::WindowSystem(Options& o) { + // Open the X11 display: + dpy = XOpenDisplay(o.dpyName.c_str()); + if (!dpy) + throw CantOpenDisplay(); + + // Verify that GLX is supported: + int error_base, event_base; + if (glXQueryExtension(dpy, &error_base, &event_base) == False) + throw NoOpenGL(); + + // Record version numbers for later use: + if (glXQueryVersion(dpy, &GLXVersMajor, &GLXVersMinor) == False) + throw Error(); // this should never happen :-) + + // Get the list of raw XVisualInfo structures: + XVisualInfo vit; + vit.screen = DefaultScreen(dpy); + int n; + vip = XGetVisualInfo(dpy, VisualScreenMask, &vit, &n); + + // Construct a vector of DrawingSurfaceConfigs corresponding to the + // XVisualInfo structures that indicate they support OpenGL: + vector<DrawingSurfaceConfig*> glxv; + for (int i = 0; i < n; ++i) { + int supportsOpenGL; + glXGetConfig(dpy, &vip[i], GLX_USE_GL, &supportsOpenGL); + if (supportsOpenGL) + glxv.push_back(new DrawingSurfaceConfig (dpy, &vip[i])); + } + + // Filter the basic list of DrawingSurfaceConfigs according to + // constraints provided by the user. (This makes it convenient + // to run tests on just a subset of all available configs.) + DrawingSurfaceFilter f(o.visFilter); // may throw an exception! + surfConfigs = f.filter(glxv); +} // WindowSystem::WindowSystem + +#elif defined(__WIN__) +WindowSystem::WindowSystem(Options& o) { + // register an window class + WNDCLASS wc; + + wc.style = CS_OWNDC; + wc.lpfnWndProc = Window::WindowProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = GetModuleHandle(NULL); + wc.hIcon = LoadIcon(wc.hInstance, "glean"); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); + wc.lpszMenuName = NULL; + wc.lpszClassName = "glean"; + + if (!RegisterClass(&wc)) + throw Error(); + + + HDC hDC = GetDC(GetDesktopWindow()); + + PIXELFORMATDESCRIPTOR pfd; + int n = DescribePixelFormat(hDC,0,sizeof(pfd),0); + + vector<DrawingSurfaceConfig*> glpf; + + for (int i = 1;i <= n;++i) { + DescribePixelFormat(hDC,i,sizeof(pfd),&pfd); + + glpf.push_back(new DrawingSurfaceConfig(i,&pfd)); + } + + ReleaseDC(GetDesktopWindow(),hDC); + + // Filter the basic list of DrawingSurfaceConfigs according to + // constraints provided by the user. (This makes it convenient + // to run tests on just a subset of all available configs.) + DrawingSurfaceFilter f(o.visFilter); // may throw an exception! + surfConfigs = f.filter(glpf); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Destructors +/////////////////////////////////////////////////////////////////////////////// +#if defined(__X11__) +WindowSystem::~WindowSystem() { + XFree(vip); +} // WindowSystem:: ~WindowSystem + +#elif defined(__WIN__) +WindowSystem::~WindowSystem() { +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// makeCurrent and friends - binding contexts and drawing surfaces +/////////////////////////////////////////////////////////////////////////////// + +bool +WindowSystem::makeCurrent() { +# if defined(__X11__) +# if defined(GLX_VERSION_1_3) +# error "XXX Need to write GLX 1.3 MakeCurrent code" +# else + return glXMakeCurrent(dpy, None, 0); +# endif +# elif defined(__WIN__) + return wglMakeCurrent(0,0); +# endif +} // WindowSystem::makeCurrent + +bool +WindowSystem::makeCurrent(RenderingContext& r, Window& w) { +# if defined(__X11__) +# if defined(GLX_VERSION_1_3) +# error "XXX Need to write GLX 1.3 MakeCurrent code" +# else + return glXMakeCurrent(dpy, w.xWindow, r.rc); +# endif +# elif defined(__WIN__) + return wglMakeCurrent(w.get_dc(),r.rc); +# endif +} // WindowSystem::makeCurrent + +void +WindowSystem::quiesce() { +# if defined(__X11__) + XSync(dpy, False); +# elif defined(__WIN__) +# endif +} // WindowSystem::quiesce + +} // namespace GLEAN diff --git a/src/glean/winsys.h b/src/glean/winsys.h new file mode 100644 index 0000000..036954e --- /dev/null +++ b/src/glean/winsys.h @@ -0,0 +1,111 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// winsys.h: facade for common window-system operations + +// This class and related classes provide window system operations +// that are sufficient to support most basic rendering tests. These +// operations include initializing the window system, creating and +// destroying windows and rendering contexts, selecting pixel +// configurations, etc. + +// Tests using this set of classes for all window system services are +// ``portable'' in a useful sense. Not all tests are portable, +// however; in particular, tests of window-system-specific +// functionality must execute window system commands directly. Such +// tests may require access to class members that would ideally be +// private; for example, the X11 Display pointer. Thus most members +// of this class are public. + + + +#ifndef __winsys_h__ +#define __winsys_h__ + +#include <string> +#include <vector> +#include "glwrap.h" + +namespace GLEAN { + +class DrawingSurface; // Forward and mutually-recursive references. +class Window; +class DrawingSurfaceConfig; +class RenderingContext; +class Options; + +class WindowSystem { + public: + // Constructors/Destructor: + + WindowSystem(Options& o); + ~WindowSystem(); + + // Exceptions: + + struct Error { }; // Base class for window system errors. + struct CantOpenDisplay: public Error { // Can't initialize display. + }; + struct NoOpenGL: public Error { // Missing GLX, WGL, etc. + }; + + // Utilities: + + bool makeCurrent(); // Remove context/surface binding. + bool makeCurrent(RenderingContext& r, Window& w); + // Bind given context and surface. + void quiesce(); // Wait for system to go idle. + + // State information: + + vector<DrawingSurfaceConfig*> surfConfigs; + // All available drawing surface configurations. + vector<DrawingSurface*> surfaces; + // All currently-active surfaces. + vector<RenderingContext*> contexts; + // All currently-active rendering contexts. + +# if defined(__X11__) + Display* dpy; // Pointer to X11 Display structure. + + int GLXVersMajor; // GLX major version number. + int GLXVersMinor; // GLX minor version number. + + XVisualInfo* vip; // Array of raw XVisualInfo structures. + +# elif defined(__WIN__) +# endif + +}; // class WindowSystem + +} // namespace GLEAN + +#endif // __winsys_h__ diff --git a/src/glh/Makefile b/src/glh/Makefile new file mode 100644 index 0000000..78a11f8 --- /dev/null +++ b/src/glh/Makefile @@ -0,0 +1,5 @@ +include $(GLEAN_ROOT)/make/common.mak + +HTARGET=glwrap.h + +include $(GLEAN_ROOT)/make/h.mak diff --git a/src/glh/glwrap.h b/src/glh/glwrap.h new file mode 100644 index 0000000..da63336 --- /dev/null +++ b/src/glh/glwrap.h @@ -0,0 +1,63 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// Microsoft's version of gl.h invokes macros that are defined in +// windows.h. To avoid a conditional #include <windows.h> in +// every file, we wrap gl.h with the proper conditions here, and +// have our source files #include "glwrap.h" instead. + +// As a bonus we ensure that all declarations for GLU are included, +// and on X11-based systems, we cover X11 and GLX as well. This +// should cover nearly everything needed by a typical glean test. + +// It's unfortunate that both Windows and Xlib are so casual about +// polluting the global namespace. The problem isn't easily resolved, +// even with the use of C++ namespace directives, because (a) macros +// in the include files refer to unqualified global variables, and (b) +// preprocessor macros themselves aren't affected by namespaces. + + +#ifndef __glwrap_h__ +#define __glwrap_h__ + +#if defined(__WIN__) +# include <windows.h> +# include <GL/gl.h> +# include <GL/glu.h> +#elif defined(__X11__) +# include <GL/glx.h> + // glx.h covers Xlib.h and gl.h, among others +# include <GL/glu.h> +#else +# error "Improper window system configuration; must be __WIN__ or __X11__." +#endif + +#endif __glwrap_h__ diff --git a/src/glh/makefile.win b/src/glh/makefile.win new file mode 100644 index 0000000..3400688 --- /dev/null +++ b/src/glh/makefile.win @@ -0,0 +1,12 @@ +.SILENT : + +!INCLUDE $(GLEAN_ROOT)\make\common.win + +ALL : + echo Copying Header Files + copy .\glwrap.h $(GLEAN_INC_DIR) + +CLEAN : + + + diff --git a/src/libs/Makefile b/src/libs/Makefile new file mode 100644 index 0000000..58b1c5d --- /dev/null +++ b/src/libs/Makefile @@ -0,0 +1,5 @@ +include $(GLEAN_ROOT)/make/common.mak + +DIRS=rand timer stats image lex dsurf + +include $(GLEAN_ROOT)/make/null.mak diff --git a/src/libs/dsurf/Makefile b/src/libs/dsurf/Makefile new file mode 100644 index 0000000..b4ba475 --- /dev/null +++ b/src/libs/dsurf/Makefile @@ -0,0 +1,6 @@ +include $(GLEAN_ROOT)/make/common.mak + +TARGET=libdsurf.a +HTARGET=dsconfig.h dsfilt.h + +include $(GLEAN_ROOT)/make/lib.mak diff --git a/src/libs/dsurf/dsconfig.cpp b/src/libs/dsurf/dsconfig.cpp new file mode 100644 index 0000000..81fbaba --- /dev/null +++ b/src/libs/dsurf/dsconfig.cpp @@ -0,0 +1,767 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + +// dsconfig.cpp: Implementation of drawing surface configuration utilities +#include "dsconfig.h" +#include <iostream> +#include <strstream> +#include <string.h> +#include <map> +#include <limits.h> + +#ifdef __WIN__ +// disable the annoying warning : "forcing value to bool 'true' or 'false' (performance warning)" +#pragma warning (disable : 4800) +#endif + + +#include "lex.h" + + + +namespace { + +#ifdef __X11__ + +bool +haveGLXExtension(::Display* dpy, const char* extName) { + const char* extString = + glXQueryExtensionsString(dpy, DefaultScreen(dpy)); + // We don't cache the result, so that subsequent calls + // with different values of ``dpy'' will work correctly. + // Would be nice to improve this, though. + + const char* start = extString; + for (;;) { + const char* where = strstr(start, extName); + if (!where) + return false; + + // Make sure we're not fooled by extensions whose names + // have the desired extName as an initial substring: + const char* terminator = where + strlen(extName); + if ((where == start || where[-1] == ' ') + && (*terminator == ' ' || *terminator == 0)) + return true; + + start = terminator; + } + + return false; +} // haveGLXExtension + +#endif + +typedef enum { // These variable tags are used as array indices, + // so they should represent a small dense set of + // nonnegative integers. 0 is reserved. + VID = 1, + VFBCID, + VCANRGBA, + VR, + VG, + VB, + VA, + VCANCI, + VBUFSIZE, + VLEVEL, + VDB, + VSTEREO, + VAUX, + VZ, + VS, + VACCUMR, + VACCUMG, + VACCUMB, + VACCUMA, + VCANWINDOW, + VCANPIXMAP, + VCANPBUFFER, + VMAXPBUFFERWIDTH, + VMAXPBUFFERHEIGHT, + VMAXPBUFFERPIXELS, + VCANWINSYSRENDER, + VFAST, + VCONFORMANT, + VTRANSPARENT, + VTRANSR, + VTRANSG, + VTRANSB, + VTRANSA, + VTRANSI, + V_LAST +} CanonVar; + +struct {CanonVar var; char* name;} varNames[] = { + {VID, "id"}, + {VFBCID, "fbcID"}, + {VCANRGBA, "canRGBA"}, + {VR, "r"}, + {VG, "g"}, + {VB, "b"}, + {VA, "a"}, + {VCANCI, "canCI"}, + {VBUFSIZE, "bufSize"}, + {VLEVEL, "level"}, + {VDB, "db"}, + {VSTEREO, "stereo"}, + {VAUX, "aux"}, + {VZ, "z"}, + {VS, "s"}, + {VACCUMR, "accumR"}, + {VACCUMG, "accumG"}, + {VACCUMB, "accumB"}, + {VACCUMA, "accumA"}, + {VCANWINDOW, "window"}, + {VCANPIXMAP, "pixmap"}, + {VCANPBUFFER, "pBuffer"}, + {VMAXPBUFFERWIDTH, "maxPBufferWidth"}, + {VMAXPBUFFERHEIGHT, "maxPBufferHeight"}, + {VMAXPBUFFERPIXELS, "maxPBufferPixels"}, + {VCANWINSYSRENDER, "winsys"}, + {VFAST, "fast"}, + {VCONFORMANT, "conformant"}, + {VTRANSPARENT, "transparent"}, + {VTRANSR, "transR"}, + {VTRANSG, "transG"}, + {VTRANSB, "transB"}, + {VTRANSA, "transA"}, + {VTRANSI, "transI"} +}; + +char* mapVarToName[V_LAST]; +map<string, CanonVar> mapNameToVar; +bool mapsInitialized = false; + +void +initializeMaps() { + for (unsigned i = 0; i < sizeof(varNames)/sizeof(varNames[0]); ++i) { + mapVarToName[varNames[i].var] = varNames[i].name; + mapNameToVar[varNames[i].name] = varNames[i].var; + } + mapsInitialized = true; +} // initializeMaps + +template<class T> inline T abs(T a) {return (a < 0)? -a: a;} + +} // anonymous namespace + + +namespace GLEAN { + + +#if defined(__X11__) + +DrawingSurfaceConfig::DrawingSurfaceConfig(::Display* dpy, ::XVisualInfo* pvi) { + if (!mapsInitialized) + initializeMaps(); + + int var; + + vi = pvi; + visID = vi->visualid; + + glXGetConfig(dpy, vi, GLX_RGBA, &var); + canRGBA = var; + canCI = !var; + // There is no dual-personality Visual support in early + // versions of GLX. + + glXGetConfig(dpy, vi, GLX_BUFFER_SIZE, &bufSize); + + glXGetConfig(dpy, vi, GLX_LEVEL, &level); + + glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &var); + db = var; + + glXGetConfig(dpy, vi, GLX_STEREO, &var); + stereo = var; + + glXGetConfig(dpy, vi, GLX_AUX_BUFFERS, &aux); + + if (canRGBA) { + glXGetConfig(dpy, vi, GLX_RED_SIZE, &r); + glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &g); + glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &b); + glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &a); + } else + r = g = b = a = 0; + + glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &z); + + glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &s); + + if (canRGBA) { + glXGetConfig(dpy, vi, GLX_ACCUM_RED_SIZE, &accR); + glXGetConfig(dpy, vi, GLX_ACCUM_GREEN_SIZE, &accG); + glXGetConfig(dpy, vi, GLX_ACCUM_BLUE_SIZE, &accB); + glXGetConfig(dpy, vi, GLX_ACCUM_ALPHA_SIZE, &accA); + } else + accR = accG = accB = accA = 0; + + canWindow = canPixmap = true; + // Only guaranteed in early versions of GLX. + + canWinSysRender = true; + // Only guaranteed in early versions of GLX. + + fast = true; + conformant = true; +# if defined(GLX_EXT_visual_rating) + if (haveGLXExtension(dpy, "EXT_visual_rating")) { + glXGetConfig(dpy, vi, GLX_VISUAL_CAVEAT_EXT, &var); + if (var == GLX_SLOW_VISUAL_EXT) + fast = false; + else if (var == GLX_NON_CONFORMANT_VISUAL_EXT) + conformant = false; + } +# endif + + transparent = false; + transR = transG = transB = transA = transI = 0; +# if defined(GLX_EXT_visual_info) + if (haveGLXExtension(dpy, "EXT_visual_info")) { + glXGetConfig(dpy, vi, GLX_TRANSPARENT_TYPE_EXT, &var); + if (var == GLX_TRANSPARENT_RGB_EXT) { + glXGetConfig(dpy, vi, + GLX_TRANSPARENT_RED_VALUE_EXT, &transR); + glXGetConfig(dpy, vi, + GLX_TRANSPARENT_GREEN_VALUE_EXT, &transG); + glXGetConfig(dpy, vi, + GLX_TRANSPARENT_BLUE_VALUE_EXT, &transB); + glXGetConfig(dpy, vi, + GLX_TRANSPARENT_ALPHA_VALUE_EXT, &transA); + } else + glXGetConfig(dpy, vi, + GLX_TRANSPARENT_INDEX_VALUE_EXT, &transI); + } +# endif +} // DrawingSurfaceConfig::DrawingSurfaceConfig + +#if defined(GLX_VERSION_1_3) +DrawingSurfaceConfig::DrawingSurfaceConfig(::Display* dpy, ::GLXFBConfig* pfbc) +{ + if (!mapsInitialized) + initializeMaps(); +} // DrawingSurfaceConfig::DrawingSurfaceConfig +#error "XXX Need to write drawing surface config code for GLX 1.3" +#endif + +#elif defined(__WIN__) + +DrawingSurfaceConfig::DrawingSurfaceConfig(int id, ::PIXELFORMATDESCRIPTOR *ppfd) +{ + if (!mapsInitialized) + initializeMaps(); + + pfd = ppfd; + pfdID = id; + + canRGBA = pfd->iPixelType == PFD_TYPE_RGBA; + canCI = pfd->iPixelType == PFD_TYPE_COLORINDEX; + + bufSize = pfd->cColorBits + pfd->cAlphaBits; + + level = 0; + + db = pfd->dwFlags & PFD_DOUBLEBUFFER; + + stereo = pfd->dwFlags & PFD_STEREO; + + aux = pfd->cAuxBuffers; + + if (canRGBA) { + r = pfd->cRedBits; + g = pfd->cGreenBits; + b = pfd->cBlueBits; + a = pfd->cAlphaBits; + } + else + r = g = b = a = 0; + + z = pfd->cDepthBits; + s = pfd->cStencilBits; + + accR = pfd->cAccumRedBits; + accG = pfd->cAccumGreenBits; + accB = pfd->cAccumBlueBits; + accA = pfd->cAccumAlphaBits; + + canWindow = pfd->dwFlags & PFD_DRAW_TO_WINDOW; + + canWinSysRender = pfd->dwFlags & PFD_SUPPORT_GDI; + + if (pfd->dwFlags & PFD_GENERIC_FORMAT) + { + if (pfd->dwFlags & PFD_GENERIC_ACCELERATED) + { + // it's an MCD - at least it has some acceleration + fast = true; + } + else + { + // it's software + fast = false; + } + } + else + { + // it's an ICD + fast = true; + } + + // we'll assume that the OpenGL implementation thinks it is conformant + conformant = true; + + // chromakeying isn't supported + transparent = false; + transR = transG = transB = transA = transI = 0; +} + +#endif + + +DrawingSurfaceConfig::DrawingSurfaceConfig(string& str) { + if (!mapsInitialized) + initializeMaps(); + + try { + Lex lex(str.c_str()); + + for (lex.next(); lex.token != Lex::END; lex.next()) { + if (lex.token != Lex::ID) + throw Syntax("expected variable name", + lex.position()); + lex.next(); + if (lex.token != Lex::ICONST) + throw Syntax("expected integer value", + lex.position()); + + // Yes, this is an unpleasantly verbose way to + // handle this problem. However, it will be + // necessary when we have to deal with attributes + // that aren't all of a simple integral type. + + switch (mapNameToVar[lex.id]) { + case VID: +# if defined(__X11__) + visID = lex.iValue; +# endif + break; + case VFBCID: +# if defined(GLX_VERSION_1_3) + fbcID = lex.iValue; +# endif + break; + case VCANRGBA: + canRGBA = lex.iValue; + break; + case VR: + r = lex.iValue; + break; + case VG: + g = lex.iValue; + break; + case VB: + b = lex.iValue; + break; + case VA: + a = lex.iValue; + break; + case VCANCI: + canCI = lex.iValue; + break; + case VBUFSIZE: + bufSize = lex.iValue; + break; + case VLEVEL: + level = lex.iValue; + break; + case VDB: + db = lex.iValue; + break; + case VSTEREO: + stereo = lex.iValue; + break; + case VAUX: + aux = lex.iValue; + break; + case VZ: + z = lex.iValue; + break; + case VS: + s = lex.iValue; + break; + case VACCUMR: + accR = lex.iValue; + break; + case VACCUMG: + accG = lex.iValue; + break; + case VACCUMB: + accB = lex.iValue; + break; + case VACCUMA: + accA = lex.iValue; + break; + case VCANWINDOW: + canWindow = lex.iValue; + break; + case VCANPIXMAP: +# if defined(__X11__) + canPixmap = lex.iValue; +# endif + break; + case VCANPBUFFER: +# if defined(GLX_VERSION_1_3) + canPBuffer = lex.iValue; +# endif + break; + case VMAXPBUFFERWIDTH: +# if defined(GLX_VERSION_1_3) + maxPBufferWidth = lex.iValue; +# endif + break; + case VMAXPBUFFERHEIGHT: +# if defined(GLX_VERSION_1_3) + maxPBufferHeight = lex.iValue; +# endif + break; + case VMAXPBUFFERPIXELS: +# if defined(GLX_VERSION_1_3) + maxPBufferPixels = lex.iValue; +# endif + break; + case VCANWINSYSRENDER: + canWinSysRender = lex.iValue; + break; + case VFAST: + fast = lex.iValue; + break; + case VCONFORMANT: + conformant = lex.iValue; + break; + case VTRANSPARENT: + transparent = lex.iValue; + break; + case VTRANSR: + transR = lex.iValue; + break; + case VTRANSG: + transG = lex.iValue; + break; + case VTRANSB: + transB = lex.iValue; + break; + case VTRANSA: + transA = lex.iValue; + break; + case VTRANSI: + transI = lex.iValue; + break; + default: + throw Syntax("unrecognized variable", + lex.position()); + } + } + } + catch (Lex::Lexical e) { + throw Syntax(e.err, e.position); + } +} // DrawingSurfaceConfing::DrawingSurfaceConfig + + +/////////////////////////////////////////////////////////////////////////////// +// canonicalDescription - return a description string that can be used +// to reconstruct the essential attributes of a drawing surface +// configuration. Note that visual ID numbers are included for +// completeness, but they must be ignored when attempting to compare +// two surface configurations; there's no guarantee that they'll be +// valid (or even relevant, since they may have been created on another +// OS). +// +// This is ugly code, but it keeps the names used here and in +// the string-based constructor for DrawingSurfaceConfig in sync +// automatically. +/////////////////////////////////////////////////////////////////////////////// +string +DrawingSurfaceConfig::canonicalDescription() { + + // Would rather use ostringstream, but it's not available in + // egcs 1.1.2. + char buf[1024]; + ostrstream s(buf, sizeof(buf)); + +# if defined(__X11__) + s << mapVarToName[VID] << ' ' << visID; +# if defined(GLX_VERSION_1_3) + s << ' ' << mapVarToName[VFBCID] << ' ' << fbcID; +# endif +# elif defined(__WIN__) + s << mapVarToName[VID] << ' ' << pfdID; +# endif + + s << ' ' << mapVarToName[VCANRGBA] << ' ' << canRGBA; + s << ' ' << mapVarToName[VR] << ' ' << r + << ' ' << mapVarToName[VG] << ' ' << g + << ' ' << mapVarToName[VB] << ' ' << b + << ' ' << mapVarToName[VA] << ' ' << a; + + s << ' ' << mapVarToName[VCANCI] << ' ' << canCI; + + s << ' ' << mapVarToName[VBUFSIZE] << ' ' << bufSize; + + s << ' ' << mapVarToName[VLEVEL] << ' ' << level; + + s << ' ' << mapVarToName[VDB] << ' ' << db; + + s << ' ' << mapVarToName[VSTEREO] << ' '<< stereo; + + s << ' ' << mapVarToName[VAUX] << ' ' << aux; + + s << ' ' << mapVarToName[VZ] << ' ' << z; + + s << ' ' << mapVarToName[VS] << ' ' << DrawingSurfaceConfig::s; + + s << ' ' << mapVarToName[VACCUMR] << ' ' << accR + << ' ' << mapVarToName[VACCUMG] << ' ' << accG + << ' ' << mapVarToName[VACCUMB] << ' ' << accB + << ' ' << mapVarToName[VACCUMA] << ' ' << accA; + + s << ' ' << mapVarToName[VCANWINDOW] << ' ' << canWindow; + +# if defined(__X11__) + s << ' ' << mapVarToName[VCANPIXMAP] << ' ' << canPixmap; + +# if defined(GLX_VERSION_1_3) + s << ' ' << mapVarToName[VCANPBUFFER] << ' ' << canPBuffer; + s << ' ' << mapVarToName[VMAXPBUFFERWIDTH] << ' ' << maxPBufferWidth; + s << ' ' << mapVarToName[VMAXPBUFFERHEIGHT] << ' ' << maxPBufferHeight; + s << ' ' << mapVarToName[VMAXPBUFFERPIXELS] << ' ' << maxPBufferPixels; +# endif + +# endif + + s << ' ' << mapVarToName[VCANWINSYSRENDER] << ' ' << canWinSysRender; + + s << ' ' << mapVarToName[VFAST] << ' ' << fast; + + s << ' ' << mapVarToName[VCONFORMANT] << ' ' << conformant; + + s << ' ' << mapVarToName[VTRANSPARENT] << ' ' << transparent; + s << ' ' << mapVarToName[VTRANSR] << ' ' << transR + << ' ' << mapVarToName[VTRANSG] << ' ' << transG + << ' ' << mapVarToName[VTRANSB] << ' ' << transB + << ' ' << mapVarToName[VTRANSA] << ' ' << transA + << ' ' << mapVarToName[VTRANSI] << ' ' << transI; + + s << '\0'; + return s.str(); +} // DrawingSurfaceConfig::canonicalDescription + +/////////////////////////////////////////////////////////////////////////////// +// conciseDescription - return a description string that's appropriate for +// reading by humans, rather than parsing by machine. +/////////////////////////////////////////////////////////////////////////////// +string +DrawingSurfaceConfig::conciseDescription() { + char buf[1024]; + ostrstream s(buf, sizeof(buf)); + + if (canRGBA && canCI) + s << "dual "; + + if (canRGBA) { + if (a) { + if (r == g && g == b && b == a) + s << "rgba" << r; + else + s << "r" << r << "g" << g << "b" << b + << "a" << a; + } else { + if (r == g && g == b) + s << "rgb" << r; + else + s << "r" << r << "g" << g << "b" << b; + } + } + + if (canCI) { + if (canRGBA) s << "+"; + s << "ci" << bufSize; + } + + if (level < 0) + s << ", underlay"; + else if (level > 0) + s << ", overlay"; + + if (db) + s << ", db"; + + if (stereo) + s << ", stereo"; + + if (aux) + s << ", aux" << aux; + + if (z) + s << ", z" << z; + + if (DrawingSurfaceConfig::s) + s << ", s" << DrawingSurfaceConfig::s; + + if (accR) { + if (accA) { + if (accR == accG && accG == accB + && accB == accA) + s << ", accrgba" << accR; + else + s << ", accr" << accR << "g" << accG + << "b" << accB << "a" << accA; + } else { + if (accR == accG && accG == accB) + s << ", accrgb" << accR; + else + s << ", accr" << accR << "g" << accG + << "b" << accB; + } + } + + { + s << ", "; + bool sep = false; + if (canWindow) { + s << "win"; + sep = true; + } +# if defined(__X11__) + if (canPixmap) { + if (sep) + s << "+"; + s << "pmap"; + sep = true; + } +# endif +# if defined(GLX_VERSION_1_3) + if (canPBuffer) { + if (sep) + s << "+"; + s << "pbuf"; + } +# endif + } + + if (!fast) + s << ", slow"; + + if (!conformant) + s << ", nonconformant"; + + if (transparent) { + if (canRGBA) { + s << ", transrgba (" << transR << "," << transG + << "," << transB << "," << transA << ")"; + } + if (canCI) { + s << ", transci (" << transI << ")"; + } + } + +# if defined(__X11__) +# if defined(GLX_VERSION_1_3) + s << ", id " << fbcID; +# else + s << ", id " << visID; +# endif +# elif defined(__WIN__) + s << ", id " << pfdID; +# endif + + s << '\0'; + return s.str(); +} // DrawingSurfaceConfig::conciseDescription + +/////////////////////////////////////////////////////////////////////////////// +// match - select a config that ``matches'' the current config. +// To keep this problem manageable, we'll assume that both the config +// to be matched (call it the ``A'' config) and the vector of configs to +// choose from (call them the ``B'' configs) were selected by a test +// using a single filter. Thus we can ignore any differences in buffer +// availability (because we know those are irrelevant to the test), and +// concentrate on picking configs for which the available buffers are +// (in some sense) closest in size. +// +// This will not be an acceptable solution in all cases, but it should +// suffice for many. +/////////////////////////////////////////////////////////////////////////////// +int +DrawingSurfaceConfig::match(vector<DrawingSurfaceConfig*>& choices) { + typedef vector<DrawingSurfaceConfig*>::iterator cptr; + + int best = -1; + int bestError = INT_MAX; + + for (cptr p = choices.begin(); p < choices.end(); ++p) { + DrawingSurfaceConfig& c = **p; + int error = 0; + + if (bufSize && c.bufSize) + error += abs(bufSize - c.bufSize); + if (r && c.r) + error += abs(r - c.r); + if (g && c.g) + error += abs(g - c.g); + if (b && c.b) + error += abs(b - c.b); + if (a && c.a) + error += abs(a - c.a); + if (z && c.z) + error += abs(z - c.z); + if (s && c.s) + error += abs(s - c.s); + if (accR && c.accR) + error += abs(accR - c.accR); + if (accG && c.accG) + error += abs(accG - c.accG); + if (accB && c.accB) + error += abs(accB - c.accB); + if (accA && c.accA) + error += abs(accA - c.accA); + + if (error < bestError) { + bestError = error; + best = static_cast<int>(p - choices.begin()); + } + } + + return best; +} // DrawingSurfaceConfig::match + +} // namespace GLEAN diff --git a/src/libs/dsurf/dsconfig.h b/src/libs/dsurf/dsconfig.h new file mode 100644 index 0000000..bc63b74 --- /dev/null +++ b/src/libs/dsurf/dsconfig.h @@ -0,0 +1,203 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// dsconfig.h: Drawing surface configuration utilities + +// This class abstracts the basic characteristics of drawing surfaces +// (size, depth, ancillary buffers, etc.) and operations on them. It +// serves as a wrapper for X11 Visual and FBConfig information on +// X11-based systems, and PixelFormatDescriptor information on +// Win32-based systems. + + +#ifndef __dsconfig_h__ +#define __dsconfig_h__ + +#include <string> +#include <vector> +#include "glwrap.h" + +using namespace std; + +namespace GLEAN { + +class DrawingSurfaceConfig { + public: + + // Constructors/Destructor: + +# if defined(__X11__) + DrawingSurfaceConfig(::Display* dpy, ::XVisualInfo* pvi); +# if defined(GLX_VERSION_1_3) + DrawingSurfaceConfig(::Display* dpy, ::GLXFBConfig* pfbc); +# endif +# elif defined(__WIN__) + DrawingSurfaceConfig(int id, ::PIXELFORMATDESCRIPTOR *ppfd); +# endif + + DrawingSurfaceConfig(string& s); // s is a canonical description + + // Exceptions: + + struct Error { }; // Base class for errors. + struct Syntax: public Error { // Syntax error in constructor string. + const char* err; + int position; + Syntax(const char* e, int p) { + err = e; + position = p; + } + }; + + // Attributes: + +# if defined(__X11__) + ::XVisualInfo* vi; // XVisualInfo pointer + ::XID visID; // Visual ID. +# if defined(GLX_VERSION_1_3) + ::GLXFBConfig* fbc; + ::XID fbcID; // Framebuffer Config ID. +# endif +# elif defined(__WIN__) + ::PIXELFORMATDESCRIPTOR *pfd; + int pfdID; +# endif + + bool canRGBA; // Can be used with RGBA contexts. + + bool canCI; // Can be used with color index + // contexts. + + int bufSize; // Total depth of color buffer. + + int level; // Framebuffer level. + // (<0 for underlay, 0 for main, + // >0 for overlay) + + bool db; // True if double buffered. + + bool stereo; // True if stereo-capable. + + int aux; // Number of aux color buffers. + + int r; // Depth of red channel. + + int g; // Depth of green channel. + + int b; // Depth of blue channel. + + int a; // Depth of alpha channel. + + int z; // Depth of ``z'' (depth) buffer. + + int s; // Depth of stencil buffer. + + int accR; // Depth of accum buf red channel. + + int accG; // Depth of accum buf green channel. + + int accB; // Depth of accum buf blue channel. + + int accA; // Depth of accum buf alpha channel. + + bool canWindow; // True if can be used for windows. + +# if defined(__X11__) + bool canPixmap; // True if can be used for pixmaps. +# if defined(GLX_VERSION_1_3) + bool canPBuffer; // True if can be used for pbuffers. + + int maxPBufferWidth; // Maximum width of PBuffer that + // may be created with this config. + + int maxPBufferHeight; // Maximum height of PBuffer that + // may be created with this config. + + int maxPBufferPixels; // Maximum size (in pixels) of + // PBuffer that may be created with + // this config. +# endif +# endif + + bool canWinSysRender; // True if the native window system + // can render to a drawable created + // with this config. + + bool fast; // True if config is probably + // hardware accelerated. (On GLX, + // it must not be marked ``slow.'') + + bool conformant; // True if config is advertised as + // conforming to the OpenGL spec. + + bool transparent; // True if config has some pixel value + // that is transparent (e.g., for + // overlays). + + int transR; // Transparent color red value. + + int transG; // Transparent color green value. + + int transB; // Transparent color blue value. + + int transA; // Transparent color alpha value. + + int transI; // Transparent color index value. + + // Utilities: + + string canonicalDescription(); + // Return a string containing all the attributes in a + // drawing surface configuration. This allows the config + // to be stored and recreated (essentially for use by + // configuration-matching algorithms in test result + // comparisons). + + string conciseDescription(); + // Return a description string that elides default + // attribute values and expresses some attributes in + // compressed form. Intended to be more easily readable + // for humans than the canonical description, at the + // cost of some ambiguity. + + int match(vector<DrawingSurfaceConfig*>& choices); + // Find the index of the config from ``choices'' that most + // closely matches the config specified by ``*this''. + // The matching scheme is heuristic, but intended to + // be good enough that test results for configs on one + // machine may be compared with test results for + // configs on another machine. + +}; // class DrawingSurfaceConfig + +} // namespace GLEAN + +#endif // __dsconfig_h__ diff --git a/src/libs/dsurf/dsfilt.cpp b/src/libs/dsurf/dsfilt.cpp new file mode 100644 index 0000000..07b6610 --- /dev/null +++ b/src/libs/dsurf/dsfilt.cpp @@ -0,0 +1,690 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// dsfilt.cpp: Implementation of drawing surface configuration filtering + +#include <iostream> +#include <strstream> +#include <ctype.h> +#include <stdlib.h> +#include <algorithm> +#include "dsfilt.h" +#include "dsconfig.h" + +namespace GLEAN { + + +/////////////////////////////////////////////////////////////////////////////// +// Constructor: +/////////////////////////////////////////////////////////////////////////////// +DrawingSurfaceFilter::DrawingSurfaceFilter(const string& s): + lex(s.c_str(), true) { + + if (!varTableInitialized) + InitVarTable(); + + try { + GetSymbol(); + if (!ParseCriteria()) + throw Syntax("no criteria found", lex.position()); + } + catch (Lex::Lexical e) { + throw Syntax(e.err, e.position); + } + + // Make the final sort in order of increasing ID number: + EmitKey(MIN); +# if defined(GLX_VERSION_1_3) + EmitKey(VAR_FBCID); +# else + EmitKey(VAR_ID); +# endif +} // DrawingSurfaceFilter::DrawingSurfaceFilter + +/////////////////////////////////////////////////////////////////////////////// +// matches - determine if a drawing surface config matches the specified +// criteria +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::matches(DrawingSurfaceConfig& c) { + // Process the RPN expression in ``condition'', using the supplied + // drawing surface configuration to determine values of variables. + + vector<int> stack; + + for (vector<int>::const_iterator p = condition.begin(); + p < condition.end(); ++p) { + int right; + + switch (*p) { + case ADD: + right = stack.back(); stack.pop_back(); + stack.back() += right; + break; + case AND: + right = stack.back(); stack.pop_back(); + stack.back() = stack.back() && right; + break; + case DIV: + right = stack.back(); stack.pop_back(); + stack.back() = (right == 0)? 0: stack.back() / right; + break; + case EQ: + right = stack.back(); stack.pop_back(); + stack.back() = stack.back() == right; + break; + case GE: + right = stack.back(); stack.pop_back(); + stack.back() = stack.back() >= right; + break; + case GT: + right = stack.back(); stack.pop_back(); + stack.back() = stack.back() > right; + break; + case LE: + right = stack.back(); stack.pop_back(); + stack.back() = stack.back() <= right; + break; + case LT: + right = stack.back(); stack.pop_back(); + stack.back() = stack.back() < right; + break; + case MOD: + right = stack.back(); stack.pop_back(); + stack.back() = (right == 0)? 0: stack.back() % right; + break; + case MUL: + right = stack.back(); stack.pop_back(); + stack.back() *= right; + break; + case NE: + right = stack.back(); stack.pop_back(); + stack.back() = stack.back() != right; + break; + case NEGATE: + stack.back() = -stack.back(); + break; + case NOT: + stack.back() = !stack.back(); + break; + case OR: + right = stack.back(); stack.pop_back(); + stack.back() = stack.back() || right; + break; + case SUB: + right = stack.back(); stack.pop_back(); + stack.back() -= right; + break; + case CONSTANT: + stack.push_back(*++p); + break; + default: + // Must be a variable. + stack.push_back(FetchVariable(c, + static_cast<Token>(*p))); + break; + } + } + + return stack.back(); +} // DrawingSurfaceFilter::matches + +/////////////////////////////////////////////////////////////////////////////// +// filter - find and sort all matching drawing surface configurations +/////////////////////////////////////////////////////////////////////////////// +vector<DrawingSurfaceConfig*> +DrawingSurfaceFilter::filter(vector<DrawingSurfaceConfig*>& v) { + vector<DrawingSurfaceConfig*> result; + for (vector<DrawingSurfaceConfig*>::const_iterator p = v.begin(); + p < v.end(); ++p) { + if (matches(**p)) + result.push_back(*p); + } + + sort(result.begin(), result.end(), ConfigSort(sortKeys)); + return result; +} // DrawingSurfaceFilter::filter + +/////////////////////////////////////////////////////////////////////////////// +// ConfigSort operator() - sort comparison for final ordering of configurations +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ConfigSort::operator() + (const DrawingSurfaceConfig* c1, const DrawingSurfaceConfig* c2) { + for (vector<Token>::const_iterator p=keys.begin(); p<keys.end(); ++p) { + Token dir = *p++; + int d = FetchVariable(*c1, *p) - FetchVariable(*c2, *p); + if (dir == MAX) // sort largest first? + d = -d; + if (d < 0) + return true; // c1 goes before c2 + if (d > 0) + return false; // c1 goes after c2 + } + return false; // order doesn't matter +} + +/////////////////////////////////////////////////////////////////////////////// +// InitVarTable - initialize the table mapping variable names to token values +/////////////////////////////////////////////////////////////////////////////// + +map<string,DrawingSurfaceFilter::Token> DrawingSurfaceFilter::varTable; +bool DrawingSurfaceFilter::varTableInitialized; + +void +DrawingSurfaceFilter::InitVarTable() { + varTable["r"] = VAR_R; + varTable["g"] = VAR_G; + varTable["b"] = VAR_B; + varTable["a"] = VAR_A; + varTable["rgb"] = VAR_RGB; + varTable["rgba"] = VAR_RGBA; + varTable["ci"] = VAR_CI; + varTable["accumr"] = VAR_ACCUM_R; + varTable["accumg"] = VAR_ACCUM_G; + varTable["accumb"] = VAR_ACCUM_B; + varTable["accuma"] = VAR_ACCUM_A; + varTable["accumrgb"] = VAR_ACCUM_RGB; + varTable["accumrgba"] = VAR_ACCUM_RGBA; + varTable["aux"] = VAR_AUX; + varTable["db"] = VAR_DB; + varTable["sb"] = VAR_SB; + varTable["id"] = VAR_ID; + varTable["fbcid"] = VAR_FBCID; + varTable["level"] = VAR_LEVEL; + varTable["main"] = VAR_MAIN; + varTable["overlay"] = VAR_OVERLAY; + varTable["underlay"] = VAR_UNDERLAY; + varTable["mono"] = VAR_MONO; + varTable["stereo"] = VAR_STEREO; + varTable["ms"] = VAR_MS; + varTable["s"] = VAR_S; + varTable["z"] = VAR_Z; + varTable["fast"] = VAR_FAST; + varTable["conformant"] = VAR_CONFORMANT; + varTable["transparent"] = VAR_TRANSPARENT; + varTable["transr"] = VAR_TRANS_R; + varTable["transg"] = VAR_TRANS_G; + varTable["transb"] = VAR_TRANS_B; + varTable["transa"] = VAR_TRANS_A; + varTable["transci"] = VAR_TRANS_CI; + varTable["window"] = VAR_WINDOW; + varTable["pbuffer"] = VAR_PBUFFER; + varTable["pixmap"] = VAR_PIXMAP; + varTable["glonly"] = VAR_GL_ONLY; + varTable["max"] = MAX; + varTable["min"] = MIN; + + varTableInitialized = true; +} // DrawingSurfaceFilter::InitVarTable + +/////////////////////////////////////////////////////////////////////////////// +// FetchVariable - fetch the value of a variable from a +// DrawingSurfaceConfig +/////////////////////////////////////////////////////////////////////////////// + +int +DrawingSurfaceFilter::FetchVariable(const DrawingSurfaceConfig& c, Token v) { + switch (v) { + case VAR_R: + return c.r; + case VAR_G: + return c.g; + case VAR_B: + return c.b; + case VAR_A: + return c.a; + case VAR_RGB: + return min(c.r, min(c.g, c.b)); + case VAR_RGBA: + return min(c.r, min(c.g, min(c.b, c.a))); + + case VAR_CI: + return c.canCI? c.bufSize: 0; + + case VAR_ACCUM_R: + return c.accR; + case VAR_ACCUM_G: + return c.accG; + case VAR_ACCUM_B: + return c.accB; + case VAR_ACCUM_A: + return c.accA; + case VAR_ACCUM_RGB: + return min(c.accR, min(c.accG, c.accB)); + case VAR_ACCUM_RGBA: + return min(c.accR, min(c.accG, min(c.accB, c.accA))); + + case VAR_AUX: + return c.aux; + + case VAR_DB: + return c.db; + case VAR_SB: + return !c.db; + + case VAR_ID: +# if defined(__X11__) + return c.visID; +# elif defined(__WIN__) + return c.pfdID; +# endif + case VAR_FBCID: +# if defined(GLX_VERSION_1_3) + return c.fbcID; +# else + return 0; +# endif + + case VAR_LEVEL: + return c.level; + case VAR_MAIN: + return c.level == 0; + case VAR_OVERLAY: + return c.level > 0; + case VAR_UNDERLAY: + return c.level < 0; + + case VAR_MONO: + return !c.stereo; + break; + case VAR_STEREO: + return c.stereo; + + case VAR_MS: + // XXX Can't support this at the moment; have no way to + // compile or test. + return 0; + + case VAR_S: + return c.s; + + case VAR_Z: + return c.z; + + case VAR_FAST: + return c.fast; + case VAR_CONFORMANT: + return c.conformant; + + case VAR_TRANSPARENT: + return c.transparent; + case VAR_TRANS_R: + return c.transR; + case VAR_TRANS_G: + return c.transG; + case VAR_TRANS_B: + return c.transB; + case VAR_TRANS_A: + return c.transA; + case VAR_TRANS_CI: + return c.transI; + + case VAR_WINDOW: + return c.canWindow; + case VAR_PBUFFER: +# if defined(GLX_VERSION_1_3) + return c.canPBuffer; +# else + return 0; +# endif + case VAR_PIXMAP: +# if defined(__X11__) + return c.canPixmap; +# else + return 0; +# endif + + case VAR_GL_ONLY: + return !c.canWinSysRender; + + default: + throw InternalError(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// GetSymbol - Fetch next symbol from the input string +/////////////////////////////////////////////////////////////////////////////// +void +DrawingSurfaceFilter::GetSymbol() { + lex.next(); + switch(lex.token) { + case Lex::ID: + Symbol = varTable[lex.id]; + // Note: Because ERROR has value zero in the + // Token enumeration, if the user provides a + // variable that is not in varTable, then Symbol + // will be set to ERROR. + if (Symbol == ERROR) + throw Syntax("unrecognized variable", lex.position()); + break; + case Lex::ICONST: + Value = lex.iValue; + Symbol = CONSTANT; + break; + case Lex::OR_OR: + Symbol = OR; + break; + case Lex::AND_AND: + Symbol = AND; + break; + case Lex::LE: + Symbol = LE; + break; + case Lex::LT: + Symbol = LT; + break; + case Lex::GE: + Symbol = GE; + break; + case Lex::GT: + Symbol = GT; + break; + case Lex::EQ: + Symbol = EQ; + break; + case Lex::NE: + Symbol = NE; + break; + case Lex::BANG: + Symbol = NOT; + break; + case Lex::PLUS: + Symbol = ADD; + break; + case Lex::MINUS: + Symbol = SUB; + break; + case Lex::STAR: + Symbol = MUL; + break; + case Lex::SLASH: + Symbol = DIV; + break; + case Lex::PERCENT: + Symbol = MOD; + break; + case Lex::COMMA: + Symbol = SEPARATOR; + break; + case Lex::LPAREN: + Symbol = LPAREN; + break; + case Lex::RPAREN: + Symbol = RPAREN; + break; + case Lex::END: + Symbol = END; + break; + default: + throw Syntax("unrecognized symbol", lex.position()); + } + + return; +} // DrawingSurfaceFilter::GetSymbol + +/////////////////////////////////////////////////////////////////////////////// +// ParseArithExpr +// Syntax: arithExpr -> arithTerm {('+'|'-') arithTerm} +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseArithExpr() { + if (!ParseArithTerm()) + return false; + + for (;;) { + if (Symbol == ADD || Symbol == SUB) { + Token op = Symbol; + GetSymbol(); + if (!ParseArithTerm()) + throw Syntax("missing operand of + or -", + lex.position()); + Emit(op); + } else + return true; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// ParseArithFactor +// Syntax: arithFactor -> ['+'|'-'|'!'] arithPrimary +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseArithFactor() { + if (Symbol == ADD || Symbol == SUB || Symbol == NOT) { + Token op = Symbol; + GetSymbol(); + if (!ParseArithPrimary()) + throw Syntax("missing operand of unary +, -, or !", + lex.position()); + if (op == SUB) + Emit(NEGATE); + else if (op == NOT) + Emit(NOT); + return true; + } + + return ParseArithPrimary(); +} + +/////////////////////////////////////////////////////////////////////////////// +// ParseArithPrimary +// Syntax: arithPrimary -> variable | constant | '(' expression ')' +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseArithPrimary() { + if (FIRST_VAR < Symbol && Symbol < LAST_VAR) { + Emit(Symbol); + GetSymbol(); + return true; + } + + if (Symbol == CONSTANT) { + Emit(CONSTANT); + Emit(Value); + GetSymbol(); + return true; + } + + if (Symbol == LPAREN) { + GetSymbol(); + if (!ParseExpression()) + throw Syntax("missing expression after (", + lex.position()); + if (Symbol == RPAREN) { + GetSymbol(); + return true; + } else + throw Syntax("missing )", lex.position()); + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// ParseArithTerm +// Syntax: arithTerm -> arithFactor {('*'|'/'|'%') arithFactor} +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseArithTerm() { + if (!ParseArithFactor()) + return false; + + for (;;) { + if (Symbol == MUL + || Symbol == DIV + || Symbol == MOD) { + Token op = Symbol; + GetSymbol(); + if (!ParseArithFactor()) + throw Syntax("missing operand of *, /, or %", + lex.position()); + Emit(op); + } else + return true; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// ParseBoolFactor +// Syntax: boolFactor -> arithExpr [('<'|'>'|'<='|'>='|'=='|'!=') arithExpr] +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseBoolFactor() { + if (!ParseArithExpr()) + return false; + + if (Symbol == LT + || Symbol == GT + || Symbol == LE + || Symbol == GE + || Symbol == EQ + || Symbol == NE) { + Token op = Symbol; + GetSymbol(); + if (!ParseArithExpr()) + throw Syntax("missing operand of comparison", + lex.position()); + Emit(op); + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// ParseBoolTerm +// Syntax: boolTerm -> boolFactor {'&&' boolFactor} +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseBoolTerm() { + if (!ParseBoolFactor()) + return false; + + for (;;) { + if (Symbol == AND) { + GetSymbol(); + if (!ParseBoolFactor()) + throw Syntax("missing operand of &&", + lex.position()); + Emit(AND); + } else + return true; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// ParseCriteria +// Syntax: criteria -> criterion {',' criterion} +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseCriteria() { + /* Process all the user-specified conditions and sort keys: */ + if (!ParseCriterion()) + return false; + + for (;;) { + if (Symbol == SEPARATOR) { + GetSymbol(); + if (!ParseCriterion()) + throw Syntax("missing criterion after comma", + lex.position()); + Emit(AND); + } else if (Symbol == END) + return true; + else + throw Syntax("expected comma or end of criteria", + lex.position()); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// ParseCriterion +// Syntax: criterion -> sortKey | expression +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseCriterion() { + if (ParseSortKey()) + return true; + return ParseExpression(); +} + +/////////////////////////////////////////////////////////////////////////////// +// ParseExpression +// Syntax: expression -> boolTerm {'||' boolTerm} +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseExpression() { + if (!ParseBoolTerm()) + return false; + + for (;;) { + if (Symbol == OR) { + GetSymbol(); + if (!ParseBoolTerm()) + throw Syntax("missing operand of ||", + lex.position()); + Emit(OR); + } else + return true; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// ParseSortKey +// Syntax: sortKey -> ('max'|'min') variable +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseSortKey() { + if (Symbol == MAX || Symbol == MIN) { + EmitKey(Symbol); + GetSymbol(); + if (FIRST_VAR < Symbol && Symbol < LAST_VAR) { + EmitKey(Symbol); + // + // When sorting, eliminate visuals with a zero value + // for the key. This is hard to justify on grounds + // of orthogonality, but it seems to yield the right + // behavior (especially for ``min''). + // + Emit(Symbol); + GetSymbol(); + return true; + } else + throw Syntax("missing variable name after sort key", + lex.position()); + } + + return false; +} // DrawingSurfaceFilter::ParseSortKey + + +} // namespace GLEAN diff --git a/src/libs/dsurf/dsfilt.h b/src/libs/dsurf/dsfilt.h new file mode 100644 index 0000000..89c6d58 --- /dev/null +++ b/src/libs/dsurf/dsfilt.h @@ -0,0 +1,268 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// dsfilt.h: Utilities for selecting (filtering) drawing surface configs + +// Given a string representing a Boolean expression involving +// attributes of drawing surface configurations, construct an internal +// representation of the expression which can be used to find matching +// configurations. The string may also include sorting criteria that +// will be used to select the order in which matching configurations +// are returned. + +// This class accepts a superset of the criteria supported by the +// visinfo package, originally released by SGI and used in the isfast +// library (among other things). Apologies for inconsistent naming +// conventions, capitalization, redundancy, etc.; they're due to an +// incomplete translation of the old C code. Here's the original +// copyright from visinfo, just in case the lawyers are interested: + +/* + * Copyright (c) 1994 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, + * provided that (i) the above copyright notices and this permission + * notice appear in all copies of the software and related documentation, + * and (ii) the name of Silicon Graphics may not be used in any + * advertising or publicity relating to the software without the specific, + * prior written permission of Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL, + * INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + + +#ifndef __dsfilt_h__ +#define __dsfilt_h__ + +#include <string> +#include <vector> +#include <map> +#include "lex.h" + +using namespace std; + +namespace GLEAN { + +class DrawingSurfaceConfig; // forward reference + +class DrawingSurfaceFilter { + public: + + // Constructors: + + DrawingSurfaceFilter(const string& s); + // Creates a DrawingSurfaceFilter that implements the + // filtering and sorting criteria in the given string. + + // Exceptions: + + struct Error { }; // Base class for errors. + struct Syntax: public Error { // Syntax error in string. + const char* err; + int position; + Syntax(const char* e, int p) { + err = e; + position = p; + } + }; + struct InternalError: public Error { // Shouldn't happen. + }; + + // Utilities: + + bool matches(DrawingSurfaceConfig& c); + // Returns true if the given DrawingSurfaceConfig matches + // the filter criteria. + + vector<DrawingSurfaceConfig*> filter(vector<DrawingSurfaceConfig*>& v); + // Returns a vector of DrawingSurfaceConfig pointers that + // match the filter criteria, sorted according to the sorting + // criteria. + + protected: + + typedef enum { + // These are items that may appear in the parsed + // representations of the filter or sort keys. + + // First, some special cases: + ERROR = 0, // erroneous token; must appear first + END, // end of expression + + // Next, arithmetic and Boolean operators: + + ADD, // C integer + + AND, // C integer && + DIV, // C integer / + EQ, // C integer == + GE, // C integer >= + GT, // C integer > + LE, // C integer <= + LT, // C integer < + MOD, // C integer % + MUL, // C integer * + NE, // C integer != + NEGATE, // C integer unary - + NOT, // C integer unary ! + OR, // C integer || + SUB, // C integer - + SEPARATOR, // comma, separating exprs and sort keys + LPAREN, // ( + RPAREN, // ) + + // Sort keys: + + MAX, // sort largest value first + MIN, // sort smallest value first + + // Finally, operands: + + CONSTANT, // integer constants + + FIRST_VAR, // marker; starts list of variables + + VAR_R, // red channel size + VAR_G, // green channel size + VAR_B, // blue channel size + VAR_A, // alpha channel size + VAR_RGB, // min(r, g, b) + VAR_RGBA, // min(r, g, b, a) + + VAR_CI, // color index channel size + + VAR_ACCUM_R, // accum buf red channel size + VAR_ACCUM_G, // accum buf green channel size + VAR_ACCUM_B, // accum buf blue channel size + VAR_ACCUM_A, // accum buf alpha channel size + VAR_ACCUM_RGB, // min(accum r, accum g, accum b) + VAR_ACCUM_RGBA, // min(accum r, accum g, accum b, accum a) + + VAR_AUX, // number of aux color buffers + + VAR_DB, // nonzero if double buffered + VAR_SB, // nonzero if single buffered + + VAR_ID, // X11 Visual or Win32 PixelFormat ID + VAR_FBCID, // GLXFBConfig ID + + VAR_LEVEL, // framebuffer level + VAR_MAIN, // nonzero for main buffers + VAR_OVERLAY, // nonzero for overlay buffers + VAR_UNDERLAY, // nonzero for underlay buffers + + VAR_MONO, // nonzero for monoscopic buffers + VAR_STEREO, // nonzero for stereoscopic buffers + + VAR_MS, // number of multisamples + + VAR_S, // stencil buffer depth + + VAR_Z, // depth (z) buffer depth + + VAR_FAST, // nonzero if accelerated (or not marked + // ``slow'' in GLX) + + VAR_CONFORMANT, // nonzero if conforms to OpenGL spec + + VAR_TRANSPARENT, // nonzero if some pixel value is + // transparent + VAR_TRANS_R, // transparent value red component + VAR_TRANS_G, // transparent value green component + VAR_TRANS_B, // transparent value blue component + VAR_TRANS_A, // transparent value alpha component + VAR_TRANS_CI, // transparent value color index + + VAR_WINDOW, // nonzero if can be used to create a window + VAR_PBUFFER, // nonzero if can be used to create a pbuffer + VAR_PIXMAP, // nonzero if can be used to create a pixmap + // XXXWIN need VAR_BITMAP, at least; + // possibly others + + VAR_GL_ONLY, // nonzero if only OpenGL can render into + // surfaces created with this config (i.e., + // the native window system *can't* render + // into them). + + LAST_VAR // marker; ends list of variables + } Token; + + vector<int> condition; + inline void Emit(Token op) {condition.push_back(static_cast<int>(op));} + inline void Emit(int v) {condition.push_back(v);} + vector<Token> sortKeys; + inline void EmitKey(Token key) {sortKeys.push_back(key);} + + // Expression-parsing state and utilities: + Lex lex; + Token Symbol; + int Value; + static map<string,Token> varTable; + static bool varTableInitialized; + + static int FetchVariable(const DrawingSurfaceConfig& c, Token v); + static void InitVarTable(); + bool ParseArithExpr(); + bool ParseArithFactor(); + bool ParseArithPrimary(); + bool ParseArithTerm(); + bool ParseBoolFactor(); + bool ParseBoolTerm(); + bool ParseCriteria(); + bool ParseCriterion(); + bool ParseExpression(); + bool ParseSortKey(); + void GetSymbol(); + + class ConfigSort { // comparison function-object used for sorting + protected: + vector<Token>& keys; + public: + ConfigSort(vector<Token>& k): keys(k) { } + bool operator() (const DrawingSurfaceConfig* c1, + const DrawingSurfaceConfig* c2); + }; + friend class ConfigSort; + +}; // class DrawingSurfaceFilter + +} // namespace GLEAN + +#endif // __dsfilt_h__ diff --git a/src/libs/dsurf/makefile.win b/src/libs/dsurf/makefile.win new file mode 100644 index 0000000..8e301ef --- /dev/null +++ b/src/libs/dsurf/makefile.win @@ -0,0 +1,131 @@ +.SILENT : + +!IF "$(CFG)" == "" +CFG=release +!ENDIF + +!IF "$(CFG)" != "release" && "$(CFG)" != "debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "makefile.win" CFG=release +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "release" +!MESSAGE "debug" +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!INCLUDE $(GLEAN_ROOT)\make\common.win + +FTARGET=dsurf +TARGET=$(FTARGET).lib + +LIB32_OBJS= \ + "$(INTDIR)\dsconfig.obj" \ + "$(INTDIR)\dsfilt.obj" + + +!IF "$(CFG)" == "release" + +DEFINES=$(DEFINES) /D "NDEBUG" + +OUTDIR=.\Release +INTDIR=.\Release + +ALL : "$(GLEAN_LIB_DIR)\$(TARGET)" + +CLEAN : + -@erase "$(INTDIR)\*.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(OUTDIR)\$(FTARGET).pch + -@deltree /y "$(OUTDIR)" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /ML /W3 /GX /O2 $(DEFINES) $(INCLUDE_DIRS) /Fp"$(INTDIR)\$(FTARGET).pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +LIB32=link.exe -lib +LIB32_FLAGS=/nologo /out:"$(GLEAN_LIB_DIR)\$(TARGET)" + +"$(GLEAN_LIB_DIR)\$(TARGET)" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +!ELSEIF "$(CFG)" == "debug" + +DEFINES=$(DEFINES) /D "_DEBUG" + +OUTDIR=.\Debug +INTDIR=.\Debug + +ALL : "$(GLEAN_LIB_DIR)\$(TARGET)" + + +CLEAN : + -@erase "$(INTDIR)\*.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(INTDIR)\vc60.pdb" + -@erase "$(INTDIR)\$(FTARGET).pch" + -@deltree /Y $(INTDIR) + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MLd /W3 /Gm /GX /ZI /Od $(DEFINES) $(INCLUDE_DIRS) /Fp"$(INTDIR)\$(FTARGET).pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +LIB32=link.exe -lib +LIB32_FLAGS=/nologo /out:"$(GLEAN_LIB_DIR)\$(TARGET)" + +"$(GLEAN_LIB_DIR)\$(TARGET)" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +!ENDIF + +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +ALL : $(DS_POSTBUILD_DEP) + +$(DS_POSTBUILD_DEP) : "$(GLEAN_LIB_DIR)\$(TARGET)" + echo Copying Header Files + copy .\dsconfig.h $(GLEAN_INC_DIR) + copy .\dsfilt.h $(GLEAN_INC_DIR) + + + + diff --git a/src/libs/image/Makefile b/src/libs/image/Makefile new file mode 100644 index 0000000..1f7cb0c --- /dev/null +++ b/src/libs/image/Makefile @@ -0,0 +1,6 @@ +include $(GLEAN_ROOT)/make/common.mak + +TARGET=libimage.a +HTARGET=image.h + +include $(GLEAN_ROOT)/make/lib.mak diff --git a/src/libs/image/gl.cpp b/src/libs/image/gl.cpp new file mode 100644 index 0000000..89ca4ae --- /dev/null +++ b/src/libs/image/gl.cpp @@ -0,0 +1,82 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// OpenGL utility routines for images + +#include "image.h" + +namespace GLEAN { + + +/////////////////////////////////////////////////////////////////////////////// +// draw - draw image using glDrawPixels +/////////////////////////////////////////////////////////////////////////////// +void +Image::draw() { + glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); + glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, alignment()); + glDrawPixels(width(), height(), format(), type(), pixels()); +} // Image::draw + +/////////////////////////////////////////////////////////////////////////////// +// read - read image using glReadPixels +/////////////////////////////////////////////////////////////////////////////// +void +Image::read(GLint x, GLint y) { + glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE); + glPixelStorei(GL_PACK_LSB_FIRST, GL_FALSE); + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + glPixelStorei(GL_PACK_SKIP_ROWS, 0); + glPixelStorei(GL_PACK_SKIP_PIXELS, 0); + glPixelStorei(GL_PACK_ALIGNMENT, alignment()); + glReadPixels(x, y, width(), height(), format(), type(), pixels()); +} // Image::read + +/////////////////////////////////////////////////////////////////////////////// +// makeMipmaps - generate and load mipmaps for texturing +/////////////////////////////////////////////////////////////////////////////// +void +Image::makeMipmaps(GLenum internalFormat) { + glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); + glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, alignment()); + gluBuild2DMipmaps(GL_TEXTURE_2D, internalFormat, width(), height(), + format(), type(), pixels()); +} // Image::makeMipmaps + +}; // namespace GLEAN diff --git a/src/libs/image/image.h b/src/libs/image/image.h new file mode 100644 index 0000000..5217f75 --- /dev/null +++ b/src/libs/image/image.h @@ -0,0 +1,246 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// image.h: image data and attributes, image I/O + +// This class encapsulates OpenGL information related to images (size, +// format, etc.) and provides utilities for transferring images to and +// from files. + + +#ifndef __image_h__ +#define __image_h__ + +#include <string> +#include "glwrap.h" +#include "stats.h" + +namespace GLEAN { + +class Image { + + private: + + GLsizei _width; + GLsizei _height; + GLenum _format; + GLenum _type; + char* _pixels; + GLsizei _alignment; + GLsizei _rowSizeInBytes; + GLsizei _pixelSizeInBytes; + + enum { // validation bits, for lazy validation + vbRowSizeInBytes = 1, + vbPixelSizeInBytes = 2, + vbPacker = 4, + vbUnpacker = 8, + vbAll = ~0 + }; + int _invalid; + inline bool invalid(int bit) { return _invalid & bit; } + inline bool valid(int bit) { return !invalid(bit); } + inline void invalidate(int bits) { _invalid |= bits; } + inline void validate(int bits) { _invalid &= ~bits; } + + GLsizei validateRowSizeInBytes(); + GLsizei validatePixelSizeInBytes(); + + typedef void Unpacker(GLsizei n, double* rgba, char* nextPixel); + Unpacker* _unpacker; + Unpacker* validateUnpacker(); + typedef void Packer(GLsizei n, char* nextPixel, double* rgba); + Packer* _packer; + Packer* validatePacker(); + + // For now, we will require that: + // 1. All images are in native byte order (so that byte swapping + // at the OpenGL level is unnecessary). + // 2. The image width and height above describe the entire image + // (so that there is no need to specify row length + // independently). + // 3. We have no need to specify subimages at this level (so + // there is no need for SKIP_ROWS and SKIP_PIXELS attributes). + + // Should construction fix the format and type for all time? + // That would eliminate synchronization problems between data and + // descriptive information that might arise when an Image is reused, + // and might permit use of template functions instead of lots of + // switches. Probably not; it would make dynamic type assignment + // (such as reading a TIFF file) quite awkward. + + public: + + // Exceptions: + + struct Error { }; // Base class for all image errors. + struct BadFormat: public Error { // Bad image format. + GLenum format; + BadFormat(GLenum f) {format = f;} + }; + struct BadType: public Error { // Bad image type. + GLenum type; + BadType(GLenum t) {type = t;} + }; + struct CantOpen: public Error { // Can't open file. + const char* filename; + CantOpen(const char* p) {filename = p;} + }; + struct UnsupportedTIFF: public Error { // TIFF we can't handle. + }; + struct RefImageTooLarge: public Error { // Can't register ref image. + }; + + // Constructors/Destructor: + + Image(); + Image(int aWidth, int aHeight, GLenum aFormat, GLenum aType); + Image(int aWidth, int aHeight, GLenum aFormat, GLenum aType, + double r, double g, double b, double a); + Image(Image& i); + Image& operator= (Image& i); + ~Image(); + + // Reserve space for the pixel array: + + void reserve(); + + // Get/Set attributes. These attributes are useful for calls + // to glDrawPixels, glTexImage2D, etc. Note the alignment + // value; passing it to glPixelStore is essential before using + // one of the other OpenGL commands. + + inline GLsizei width() // Image width, in pixels + { return _width; } + inline void width(GLsizei w) + { _width = w; invalidate(vbRowSizeInBytes); } + + inline GLsizei height() // Image height, in pixels. + { return _height; } + inline void height(GLsizei h) + { _height = h; } + + inline GLenum format() // Image format. Currently + { return _format; } // these formats are supported: + // GL_LUMINANCE, + // GL_LUMINANCE_ALPHA, + // GL_RGB, GL_RGBA. + // It may be easiest to treat + // stencil, depth, etc. images + // as luminance images. + inline void format(GLenum f) { + _format = f; + invalidate( + vbRowSizeInBytes + | vbPixelSizeInBytes + | vbPacker + | vbUnpacker); + } + + inline GLenum type() // Pixel data type. Currently + { return _type; } // these types are supported: + // GL_BYTE, GL_UNSIGNED_BYTE, + // GL_SHORT, GL_UNSIGNED_SHORT, + // GL_INT, GL_UNSIGNED_INT, GL_FLOAT. + inline void type(GLenum t) { + _type = t; + invalidate( + vbRowSizeInBytes + | vbPixelSizeInBytes + | vbPacker + | vbUnpacker); + } + + inline char* pixels() // The pixels. + { return _pixels; } + void pixels(char* p); + + inline GLsizei alignment() // Alignment. See glPixelStore. + { return _alignment; } + inline void alignment(GLsizei a) + { _alignment = a; invalidate(vbRowSizeInBytes); } + + inline GLsizei rowSizeInBytes() { // Size of scanline, in bytes + return valid(vbRowSizeInBytes)? + _rowSizeInBytes: validateRowSizeInBytes(); + } + + inline GLsizei pixelSizeInBytes() { // Size of pixel, in bytes + return valid(vbPixelSizeInBytes)? + _pixelSizeInBytes: validatePixelSizeInBytes(); + } + + // XXX Utilities to determine component size in bits/bytes? + // XXX Component range (min neg, max neg, min pos, max pos, eps?) + + // Pixel packing/unpacking utilities: + + void unpack(GLsizei n, double* rgba, char* nextPixel); + void pack(GLsizei n, char* nextPixel, double* rgba); + + // Image registration. The utility compares a reference image + // to the current image (which must be at least as large as the + // reference image in both dimensions), determines the offset at + // which the reference image minus the current image has minimum + // mean absolute error (summed over R, G, B, and A), and returns + // an object specifying the offset and corresponding statistics. + + struct Registration { + int wOffset; // offset in width (x) + int hOffset; // offset in height (y) + BasicStats stats[4]; // stats for absolute error in + // R, G, B, and A + }; + Registration reg(Image& ref); + + // Image arithmetic + // XXX type and format conversions, with appropriate scaling. + // XXX image difference + // XXX minmax, histogram, contrast stretch? + + // TIFF I/O utilities: + + void readTIFF(const char* filename); + inline void readTIFF(const std::string& s) { readTIFF(s.c_str()); } + void writeTIFF(const char* filename); + inline void writeTIFF(const std::string& s) { writeTIFF(s.c_str()); } + + // GL operation utilities: + + void draw(); // Invoke glDrawPixels. + void read(GLint x, GLint y); // Invoke glReadPixels. + void makeMipmaps(GLenum intFormat); // Load texture mipmaps. + +}; // class Image + +} // namespace GLEAN + +#endif // __image_h__ diff --git a/src/libs/image/makefile.win b/src/libs/image/makefile.win new file mode 100644 index 0000000..b49e87e --- /dev/null +++ b/src/libs/image/makefile.win @@ -0,0 +1,135 @@ +.SILENT : + +!IF "$(CFG)" == "" +CFG=release +!ENDIF + +!IF "$(CFG)" != "release" && "$(CFG)" != "debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "makefile.win" CFG=release +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "release" +!MESSAGE "debug" +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!INCLUDE $(GLEAN_ROOT)\make\common.win + +FTARGET=image +TARGET=$(FTARGET).lib + +LIB32_OBJS= \ + "$(INTDIR)\gl.obj" \ + "$(INTDIR)\misc.obj" \ + "$(INTDIR)\pack.obj" \ + "$(INTDIR)\rdtiff.obj" \ + "$(INTDIR)\reg.obj" \ + "$(INTDIR)\unpack.obj" \ + "$(INTDIR)\wrtiff.obj" \ + +!IF "$(CFG)" == "release" + +DEFINES=$(DEFINES) /D "NDEBUG" + +OUTDIR=.\Release +INTDIR=.\Release + +ALL : "$(GLEAN_LIB_DIR)\$(TARGET)" + +CLEAN : + -@erase "$(INTDIR)\*.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(OUTDIR)\$(FTARGET).pch + -@deltree /y "$(OUTDIR)" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /ML /W3 /GX /O2 $(DEFINES) $(INCLUDE_DIRS) /Fp"$(INTDIR)\$(FTARGET).pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +LIB32=link.exe -lib +LIB32_FLAGS=/nologo /out:"$(GLEAN_LIB_DIR)\$(TARGET)" + +"$(GLEAN_LIB_DIR)\$(TARGET)" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +!ELSEIF "$(CFG)" == "debug" + +DEFINES=$(DEFINES) /D "_DEBUG" + +OUTDIR=.\Debug +INTDIR=.\Debug + +ALL : "$(GLEAN_LIB_DIR)\$(TARGET)" + + +CLEAN : + -@erase "$(INTDIR)\*.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(INTDIR)\vc60.pdb" + -@erase "$(INTDIR)\$(FTARGET).pch" + -@deltree /Y $(INTDIR) + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MLd /W3 /Gm /GX /ZI /Od $(DEFINES) $(INCLUDE_DIRS) /Fp"$(INTDIR)\$(FTARGET).pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +LIB32=link.exe -lib +LIB32_FLAGS=/nologo /out:"$(GLEAN_LIB_DIR)\$(TARGET)" + +"$(GLEAN_LIB_DIR)\$(TARGET)" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +!ENDIF + +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +ALL : $(DS_POSTBUILD_DEP) + +$(DS_POSTBUILD_DEP) : "$(GLEAN_LIB_DIR)\$(TARGET)" + echo Copying Header Files + copy .\image.h $(GLEAN_INC_DIR) + + + + + diff --git a/src/libs/image/misc.cpp b/src/libs/image/misc.cpp new file mode 100644 index 0000000..adfdc22 --- /dev/null +++ b/src/libs/image/misc.cpp @@ -0,0 +1,210 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// Implementation of image data, attribute, and I/O + +#include "image.h" +#include <string.h> + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/Destructor +/////////////////////////////////////////////////////////////////////////////// +// An empty image: +Image::Image() { + _width = _height = 0; + _format = GL_RGB; + _type = GL_UNSIGNED_BYTE; + _pixels = 0; + _alignment = 4; + _packer = 0; + _unpacker = 0; + _invalid = vbAll; +} // Image::Image + +// An unitialized image of the given type and size: +Image::Image(int aWidth, int aHeight, GLenum aFormat, GLenum aType) { + _width = aWidth; + _height = aHeight; + _format = aFormat; + _type = aType; + _pixels = 0; + _alignment = 4; + _packer = 0; + _unpacker = 0; + _invalid = vbAll; + reserve(); +} // Image::Image(aWidth, aHeight, aFormat, aType) + +// An image of the given type and size, initialized to a solid color: +Image::Image(int aWidth, int aHeight, GLenum aFormat, GLenum aType, + double r, double g, double b, double a) { + _width = aWidth; + _height = aHeight; + _format = aFormat; + _type = aType; + _pixels = 0; + _alignment = 4; + _packer = 0; + _unpacker = 0; + _invalid = vbAll; + reserve(); + int i; // VC++ 6 doesn't handle the definition of variables in a + // for-statement properly + + double* solidColor = new double[4 * width()]; + for (/*int */i = 0; i < 4 * width(); i += 4) { + solidColor[i + 0] = r; + solidColor[i + 1] = g; + solidColor[i + 2] = b; + solidColor[i + 3] = a; + } + + char* row = pixels(); + for (/*int */i = 0; i < height(); ++i) { + pack(width(), row, solidColor); + row += rowSizeInBytes(); + } +} // Image::Image(aWidth, aHeight, aFormat, aType) + +// Copy constructor: +Image::Image(Image& i) { + _width = i.width(); + _height = i.height(); + _format = i.format(); + _type = i.type(); + _alignment = i.alignment(); + _pixels = 0; + _packer = 0; + _unpacker = 0; + _invalid = vbAll; + reserve(); + memcpy(pixels(), i.pixels(), height() * rowSizeInBytes()); +} // Image::Image(Image&) + +/*Image::*/Image& +Image::operator= (Image& i) { + if (this == &i) + return *this; + width(i.width()); + height(i.height()); + format(i.format()); + type(i.type()); + alignment(i.alignment()); + _invalid = vbAll; + reserve(); + memcpy(pixels(), i.pixels(), height() * rowSizeInBytes()); + return *this; +} // Image::operator= + +Image::~Image() { + if (_pixels) + delete[] _pixels; +} + +/////////////////////////////////////////////////////////////////////////////// +// pixels - set pointer to pixel array +/////////////////////////////////////////////////////////////////////////////// +void +Image::pixels(char* p) { + // We always own our pixels, so delete the old ones (if any) before + // installing new ones: + if (_pixels) + delete[] _pixels; + _pixels = p; +} // Image::pixels + +/////////////////////////////////////////////////////////////////////////////// +// reserve - reserve memory for image (assuming current type, format, and size) +/////////////////////////////////////////////////////////////////////////////// +void +Image::reserve() { + pixels(0); // deallocate old pixel array + pixels(new char[height() * rowSizeInBytes()]); +} // Image::reserve + +/////////////////////////////////////////////////////////////////////////////// +// validateRowSizeInBytes - compute image row size, measured in bytes +/////////////////////////////////////////////////////////////////////////////// +GLsizei +Image::validateRowSizeInBytes() { + _rowSizeInBytes = + (width() * pixelSizeInBytes() + alignment() - 1) + & ~(alignment() - 1); + validate(vbRowSizeInBytes); + return _rowSizeInBytes; +} // Image::calcRowSizeInBytes + +/////////////////////////////////////////////////////////////////////////////// +// validatePixelSizeInBytes - compute pixel size, measured in bytes +/////////////////////////////////////////////////////////////////////////////// +GLsizei +Image::validatePixelSizeInBytes() { + switch (format()) { + case GL_LUMINANCE: + _pixelSizeInBytes = 1; + break; + case GL_LUMINANCE_ALPHA: + _pixelSizeInBytes = 2; + break; + case GL_RGB: + _pixelSizeInBytes = 3; + break; + case GL_RGBA: + _pixelSizeInBytes = 4; + break; + default: + throw BadFormat(format()); + } + + switch (type()) { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + break; + case GL_SHORT: + case GL_UNSIGNED_SHORT: + _pixelSizeInBytes <<= 1; + break; + case GL_INT: + case GL_UNSIGNED_INT: + case GL_FLOAT: + _pixelSizeInBytes <<= 2; + break; + default: + throw BadType(type()); + } + + validate(vbPixelSizeInBytes); + return _pixelSizeInBytes; +} + +}; // namespace GLEAN diff --git a/src/libs/image/pack.cpp b/src/libs/image/pack.cpp new file mode 100644 index 0000000..11617ef --- /dev/null +++ b/src/libs/image/pack.cpp @@ -0,0 +1,262 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// Data packing utilities. Note that these map component values per +// the usual OpenGL conversions. Also, see comments in unpack.cpp. + +#include "image.h" + +namespace { + +#define SCALE (static_cast<double>(num) / static_cast<double>(denom)) +#define BIAS (static_cast<double>(bias) / static_cast<double>(denom)) + +// The original implementation of packing functions (using function +// templates) wouldn't compile under VC6, but this slight variant +// using static member functions in a class template will compile. + +template<class component, int num, unsigned int denom, int bias> +class Pack +{ +public : + // pack_l + static void pack_l(GLsizei n, char* dst, double* rgba) + { + component* out = reinterpret_cast<component*>(dst); + double* end = rgba + 4 * n; + for (; rgba != end; rgba += 4) { + if (bias) + out[0] = static_cast<component>(SCALE * rgba[0] - BIAS); + else + out[0] = static_cast<component>(SCALE * rgba[0]); + out += 1; + } + } + + // pack_la + static void pack_la(GLsizei n, char* dst, double* rgba) + { + component* out = reinterpret_cast<component*>(dst); + double* end = rgba + 4 * n; + for (; rgba != end; rgba += 4) { + if (bias) { + out[0] = static_cast<component>(SCALE * rgba[0] - BIAS); + out[1] = static_cast<component>(SCALE * rgba[3] - BIAS); + } else { + out[0] = static_cast<component>(SCALE * rgba[0]); + out[1] = static_cast<component>(SCALE * rgba[3]); + } + out += 2; + } + } + + // pack_rga + static void pack_rgb(GLsizei n, char* dst, double* rgba) + { + component* out = reinterpret_cast<component*>(dst); + double* end = rgba + 4 * n; + for (; rgba != end; rgba += 4) { + if (bias) { + out[0] = static_cast<component>(SCALE * rgba[0] - BIAS); + out[1] = static_cast<component>(SCALE * rgba[1] - BIAS); + out[2] = static_cast<component>(SCALE * rgba[2] - BIAS); + } else { + out[0] = static_cast<component>(SCALE * rgba[0]); + out[1] = static_cast<component>(SCALE * rgba[1]); + out[2] = static_cast<component>(SCALE * rgba[2]); + } + out += 3; + } + } + + // pack_rgba + static void pack_rgba(GLsizei n, char* dst, double* rgba) + { + component* out = reinterpret_cast<component*>(dst); + double* end = rgba + 4 * n; + for (; rgba != end; rgba += 4) { + if (bias) { + out[0] = static_cast<component>(SCALE * rgba[0] - BIAS); + out[1] = static_cast<component>(SCALE * rgba[1] - BIAS); + out[2] = static_cast<component>(SCALE * rgba[2] - BIAS); + out[3] = static_cast<component>(SCALE * rgba[3] - BIAS); + } else { + out[0] = static_cast<component>(SCALE * rgba[0]); + out[1] = static_cast<component>(SCALE * rgba[1]); + out[2] = static_cast<component>(SCALE * rgba[2]); + out[3] = static_cast<component>(SCALE * rgba[3]); + } + out += 4; + } + } + +}; // class Pack + +#undef SCALE +#undef BIAS + +}; // anonymous namespace + + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// Public interface +/////////////////////////////////////////////////////////////////////////////// +void +Image::pack(GLsizei n, char* nextPixel, double* rgba) { + (*(valid(vbPacker)? _packer: validatePacker())) (n, nextPixel, rgba); +} + +/////////////////////////////////////////////////////////////////////////////// +// validatePacker - select appropriate pixel-packing utility +/////////////////////////////////////////////////////////////////////////////// + +Image::Packer* +Image::validatePacker() { + switch (format()) { + case GL_LUMINANCE: + switch (type()) { + case GL_BYTE: + _packer = Pack<GLbyte, 255, 2, 1>::pack_l; + break; + case GL_UNSIGNED_BYTE: + _packer = Pack<GLubyte, 255, 1, 0>::pack_l; + break; + case GL_SHORT: + _packer = Pack<GLshort, 65535, 2, 1>::pack_l; + break; + case GL_UNSIGNED_SHORT: + _packer = Pack<GLushort, 65535, 1, 0>::pack_l; + break; + case GL_INT: + _packer = Pack<GLint, 4294967295U, 2, 1>::pack_l; + break; + case GL_UNSIGNED_INT: + _packer = Pack<GLuint, 4294967295U, 1, 0>::pack_l; + break; + case GL_FLOAT: + _packer = Pack<GLfloat, 1, 1, 0>::pack_l; + break; + default: + throw BadType(type()); + } + break; + case GL_LUMINANCE_ALPHA: + switch (type()) { + case GL_BYTE: + _packer = Pack<GLbyte, 255, 2, 1>::pack_la; + break; + case GL_UNSIGNED_BYTE: + _packer = Pack<GLubyte, 255, 1, 0>::pack_la; + break; + case GL_SHORT: + _packer = Pack<GLshort, 65535, 2, 1>::pack_la; + break; + case GL_UNSIGNED_SHORT: + _packer = Pack<GLushort, 65535, 1, 0>::pack_la; + break; + case GL_INT: + _packer = Pack<GLint, 4294967295U, 2, 1>::pack_la; + break; + case GL_UNSIGNED_INT: + _packer = Pack<GLuint, 4294967295U, 1, 0>::pack_la; + break; + case GL_FLOAT: + _packer = Pack<GLfloat, 1, 1, 0>::pack_la; + break; + default: + throw BadType(type()); + } + break; + case GL_RGB: + switch (type()) { + case GL_BYTE: + _packer = Pack<GLbyte, 255, 2, 1>::pack_rgb; + break; + case GL_UNSIGNED_BYTE: + _packer = Pack<GLubyte, 255, 1, 0>::pack_rgb; + break; + case GL_SHORT: + _packer = Pack<GLshort, 65535, 2, 1>::pack_rgb; + break; + case GL_UNSIGNED_SHORT: + _packer = Pack<GLushort, 65535, 1, 0>::pack_rgb; + break; + case GL_INT: + _packer = Pack<GLint, 4294967295U, 2, 1>::pack_rgb; + break; + case GL_UNSIGNED_INT: + _packer = Pack<GLuint, 4294967295U, 1, 0>::pack_rgb; + break; + case GL_FLOAT: + _packer = Pack<GLfloat, 1, 1, 0>::pack_rgb; + break; + default: + throw BadType(type()); + } + break; + case GL_RGBA: + switch (type()) { + case GL_BYTE: + _packer = Pack<GLbyte, 255, 2, 1>::pack_rgba; + break; + case GL_UNSIGNED_BYTE: + _packer = Pack<GLubyte, 255, 1, 0>::pack_rgba; + break; + case GL_SHORT: + _packer = Pack<GLshort, 65535, 2, 1>::pack_rgba; + break; + case GL_UNSIGNED_SHORT: + _packer = Pack<GLushort, 65535, 1, 0>::pack_rgba; + break; + case GL_INT: + _packer = Pack<GLint, 4294967295U, 2, 1>::pack_rgba; + break; + case GL_UNSIGNED_INT: + _packer = Pack<GLuint, 4294967295U, 1, 0>::pack_rgba; + break; + case GL_FLOAT: + _packer = Pack<GLfloat, 1, 1, 0>::pack_rgba; + break; + default: + throw BadType(type()); + } + break; + default: + throw BadFormat(format()); + } + + validate(vbPacker); + return _packer; +} + +}; // namespace GLEAN diff --git a/src/libs/image/rdtiff.cpp b/src/libs/image/rdtiff.cpp new file mode 100644 index 0000000..5e5068f --- /dev/null +++ b/src/libs/image/rdtiff.cpp @@ -0,0 +1,156 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +#include "image.h" +#include "tiffio.h" + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// readTIFF - read image from TIFF file, set attributes to match the file +/////////////////////////////////////////////////////////////////////////////// +void +Image::readTIFF(const char* filename) { + // XXX Things we explicitly don't handle: + // Varying number of bits per sample. Sam's library doesn't + // handle that. + // Bits per sample other than 8, 16, or 32. + // Tile-oriented TIFF files. We only do strip-oriented files. + // Planar configurations other than contiguous (R,G,B,R,G,B,...). + // Premultiplied alpha. If there's a fourth color channel, + // we just assume it's non-premultiplied alpha. + // Eventually would be good to add a ``validation'' function which + // checks a file before attempting to read the image it contains. + // Also: need error-reporting code. + + TIFF* tf = TIFFOpen(filename, "r"); + if (!tf) + throw CantOpen(filename); + + uint32 u32; + + TIFFGetFieldDefaulted(tf, TIFFTAG_IMAGELENGTH, &u32); + height(u32); + + TIFFGetFieldDefaulted(tf, TIFFTAG_IMAGEWIDTH, &u32); + width(u32); + + uint16 samplesPerPixel; + TIFFGetFieldDefaulted(tf, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel); + switch (samplesPerPixel) { + case 1: + format(GL_LUMINANCE); + break; + case 2: + format(GL_LUMINANCE_ALPHA); + break; + case 3: + format(GL_RGB); + break; + case 4: + format(GL_RGBA); + break; + default: + TIFFClose(tf); + throw UnsupportedTIFF(); + } + + uint16 bitsPerSample; + TIFFGetFieldDefaulted(tf, TIFFTAG_BITSPERSAMPLE, &bitsPerSample); + uint16 sampleFormat; + if (TIFFGetField(tf, TIFFTAG_SAMPLEFORMAT, &sampleFormat) != 1) + sampleFormat = SAMPLEFORMAT_UINT; + switch ((sampleFormat << 8) | bitsPerSample) { + case (SAMPLEFORMAT_UINT << 8) | 8: + type(GL_UNSIGNED_BYTE); + break; + case (SAMPLEFORMAT_UINT << 8) | 16: + type(GL_UNSIGNED_SHORT); + break; + case (SAMPLEFORMAT_UINT << 8) | 32: + type(GL_UNSIGNED_INT); + break; + case (SAMPLEFORMAT_INT << 8) | 8: + type(GL_BYTE); + break; + case (SAMPLEFORMAT_INT << 8) | 16: + type(GL_SHORT); + break; + case (SAMPLEFORMAT_INT << 8) | 32: + type(GL_INT); + break; + case (SAMPLEFORMAT_IEEEFP << 8) | 32: + type(GL_FLOAT); + break; + default: + TIFFClose(tf); + throw UnsupportedTIFF(); + } + + // At the moment it's not obvious whether we should pad + // scanlines to achieve a preferred alignment, so we'll just + // return an alignment that matches the data. + alignment(1); + switch (rowSizeInBytes() & 0x7) { + case 0: + alignment(8); + break; + case 4: + alignment(4); + break; + case 2: + case 6: + alignment(2); + break; + case 1: + case 3: + case 5: + case 7: + alignment(1); + break; + } + + reserve(); + + { + // Store rows in reverse order, so that the default TIFF + // orientation won't result in an upside-down image for + // OpenGL: + GLsizei rowStep = rowSizeInBytes(); + char* row = pixels() + (height() - 1) * rowStep; + for (GLsizei r = 0; r < height(); ++r, row -= rowStep) + TIFFReadScanline(tf, row, r); + } + + TIFFClose(tf); +} // Image::readTIFF + +}; // namespace GLEAN diff --git a/src/libs/image/reg.cpp b/src/libs/image/reg.cpp new file mode 100644 index 0000000..da065fe --- /dev/null +++ b/src/libs/image/reg.cpp @@ -0,0 +1,178 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// Image registration. + +#include <cfloat> +#include "image.h" + +#ifdef __WIN__ +#include <cmath> // for fabs +#endif + + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// register: compare a reference image to the current (``test'') image. +// +// The reference image must be no larger than the current image, in +// both dimensions. Type doesn't matter, as both images will be +// converted to RGBA. +// +// The reference image will be slid into all possible positions over +// the current image, and the sum of the mean absolute errors for all +// four color channels computed at each position. +// +// Returns an Image::Registration struct that specifies the position at +// which the sum of mean absolute errors was minimal, plus the statistics +// at that position. +/////////////////////////////////////////////////////////////////////////////// +Image::Registration +Image::reg(Image& ref) { + int wt = width(); // Width of test image, in pixels. + int ht = height(); // Height of test image, in pixels. + int wr = ref.width(); // Width of reference image, in pixels. + int hr = ref.height(); // Height of test image, in pixels. + int dh = ht - hr; // Difference in heights, in pixels. + int dw = wt - wr; // Difference in widths, in pixels. + int i; + + if (dh < 0 || dw < 0) + throw RefImageTooLarge(); + + int wt4 = 4 * wt; // Width of test image, in RGBA samples. + int wr4 = 4 * wr; // Width of ref image, in RGBA samples. + int dw4 = 4 * dw; // Difference in widths, in samples. + + double** testPix; // Buffers containing all the rows of + // the test image that need to be + // accessed concurrently. + // XXX sure would be nice to use auto_ptr to allocate this stuff, + // but it isn't supported in the STL that came with egcs 1.1.2. + + // XXX testPix = new (double*) [dh + 1]; + // VC 6 seems to misinterpret this as a c-style cast + testPix = new double* [dh + 1]; + + + for (/*int */i = 0; i <= dh; ++i) + testPix[i] = new double [wt4]; + + double* refPix = new double [wr4]; + // Buffer containing the one row of + // the reference image that's accessed + // at any given time. + + BasicStats** stats; // Buffers containing a statistics- + // gathering structure for each of + // the possible reference image + // positions. + // XXX stats = new (BasicStats*) [dh + 1]; + // VC 6 seems to misinterpret this as a c-style cast + stats = new BasicStats * [dh + 1]; + + for (/*int*/ i = 0; i <= dh; ++i) + stats[i] = new BasicStats [dw4 + 4]; + + // Prime the pump by unpacking the first few rows of the test image: + char* testRow = pixels(); + for (/*int*/ i = 0; i < dh; ++i) { + unpack(wt, testPix[i], testRow); + testRow += rowSizeInBytes(); + } + + // Now accumulate statistics for one row of the reference image + // at a time, in all possible positions: + char* refRow = ref.pixels(); + for (/*int*/ i = 0; i < hr; ++i) { + // Get the next row of the reference image: + ref.unpack(wr, refPix, refRow); + refRow += ref.rowSizeInBytes(); + + // Get the next row of the test image: + unpack(wt, testPix[dh], testRow); + testRow += rowSizeInBytes(); + + // Accumulate absolute error for R,G,B,A in all positions: + for (int j = 0; j <= dh; ++j) + for (int k = 0; k <= dw4; k += 4) + for (int m = 0; m < wr4; m += 4) { + stats[j][k+0].sample( fabs( + refPix[m+0]-testPix[j][m+k+0])); + stats[j][k+1].sample( fabs( + refPix[m+1]-testPix[j][m+k+1])); + stats[j][k+2].sample( fabs( + refPix[m+2]-testPix[j][m+k+2])); + stats[j][k+3].sample( fabs( + refPix[m+3]-testPix[j][m+k+3])); + } + } + + // Now find the position for which the sum of the mean absolute errors + // is minimal: + double minErrorSum = DBL_MAX; + int minI = 0; + int minJ = 0; + for (/*int*/ i = 0; i <= dh; ++i) + for (int j = 0; j <= dw4; j += 4) { + double errorSum = stats[i][j+0].mean() + + stats[i][j+1].mean() + + stats[i][j+2].mean() + + stats[i][j+3].mean(); + if (errorSum < minErrorSum) { + minErrorSum = errorSum; + minI = i; + minJ = j; + } + } + + Registration r; + r.wOffset = minJ / 4; + r.hOffset = minI; + r.stats[0] = stats[minI][minJ+0]; + r.stats[1] = stats[minI][minJ+1]; + r.stats[2] = stats[minI][minJ+2]; + r.stats[3] = stats[minI][minJ+3]; + + // Clean up: + for (/*int*/ i = 0; i <= dh; ++i) + delete[] testPix[i]; + delete[] testPix; + delete[] refPix; + for (/*int*/ i = 0; i <= dh; ++i) + delete[] stats[i]; + delete[] stats; + + return r; +} // Image::register + +}; // namespace GLEAN diff --git a/src/libs/image/unpack.cpp b/src/libs/image/unpack.cpp new file mode 100644 index 0000000..5c0c9a6 --- /dev/null +++ b/src/libs/image/unpack.cpp @@ -0,0 +1,271 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// Data unpacking utilities. Note that these map component values per +// the usual OpenGL conventions. + +// XXX The construction of SCALE and BIAS is clumsy, and the need to +// test bias is really unfortunate, but egcs 1.1.2 won't propagate +// floating-point constant expressions from equivalent const +// declarations. + +#include "image.h" + +namespace { + +#define SCALE (static_cast<double>(num) / static_cast<double>(denom)) +#define BIAS (static_cast<double>(bias) / static_cast<double>(denom)) + +// See comments in pack.cpp concerning this workaround for a VC6 problem. + +template<class component, int num, unsigned int denom, int bias> +class Unpack +{ +public : + // unpack_l + static void unpack_l(GLsizei n, double* rgba, char* src) + { + component* in = reinterpret_cast<component*>(src); + // XXX It seems to me that static_cast should be sufficient, + // but egcs 1.1.2 thinks otherwise. + + double* end = rgba + 4 * n; + for (; rgba != end; rgba += 4) { + if (bias) + rgba[0] = SCALE * in[0] + BIAS; + else + rgba[0] = SCALE * in[0]; + rgba[1] = rgba[2] = rgba[3] = 0.0; + in += 1; + } + } + + // unpack_la + static void unpack_la(GLsizei n, double* rgba, char* src) + { + component* in = reinterpret_cast<component*>(src); + double* end = rgba + 4 * n; + for (; rgba != end; rgba += 4) { + if (bias) { + rgba[0] = SCALE * in[0] + BIAS; + rgba[3] = SCALE * in[1] + BIAS; + } else { + rgba[0] = SCALE * in[0]; + rgba[3] = SCALE * in[1]; + } + rgba[1] = rgba[2] = 0.0; + in += 2; + } + } + + // unpack_rgb + static void unpack_rgb(GLsizei n, double* rgba, char* src) + { + component* in = reinterpret_cast<component*>(src); + double* end = rgba + 4 * n; + for (; rgba != end; rgba += 4) { + if (bias) { + rgba[0] = SCALE * in[0] + BIAS; + rgba[1] = SCALE * in[1] + BIAS; + rgba[2] = SCALE * in[2] + BIAS; + } else { + rgba[0] = SCALE * in[0]; + rgba[1] = SCALE * in[1]; + rgba[2] = SCALE * in[2]; + } + rgba[3] = 0.0; + in += 3; + } + } + + // unpack_rgba + static void unpack_rgba(GLsizei n, double* rgba, char* src) + { + component* in = reinterpret_cast<component*>(src); + double* end = rgba + 4 * n; + for (; rgba != end; rgba += 4) { + if (bias) { + rgba[0] = SCALE * in[0] + BIAS; + rgba[1] = SCALE * in[1] + BIAS; + rgba[2] = SCALE * in[2] + BIAS; + rgba[3] = SCALE * in[3] + BIAS; + } else { + rgba[0] = SCALE * in[0]; + rgba[1] = SCALE * in[1]; + rgba[2] = SCALE * in[2]; + rgba[3] = SCALE * in[3]; + } + in += 4; + } + } + +}; // class Unpack + +#undef SCALE +#undef BIAS + +}; // anonymous namespace + + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// Public interface +/////////////////////////////////////////////////////////////////////////////// +void +Image::unpack(GLsizei n, double* rgba, char* nextPixel) { + (*(valid(vbUnpacker)? _unpacker: validateUnpacker())) + (n, rgba, nextPixel); +} + +/////////////////////////////////////////////////////////////////////////////// +// validateUnpacker - select appropriate pixel-unpacking utility +/////////////////////////////////////////////////////////////////////////////// +Image::Unpacker* +Image::validateUnpacker() { + switch (format()) { + case GL_LUMINANCE: + switch (type()) { + case GL_BYTE: + _unpacker = Unpack<GLbyte, 2, 255, 1>::unpack_l; + break; + case GL_UNSIGNED_BYTE: + _unpacker = Unpack<GLubyte, 1, 255, 0>::unpack_l; + break; + case GL_SHORT: + _unpacker = Unpack<GLshort, 2, 65535, 1>::unpack_l; + break; + case GL_UNSIGNED_SHORT: + _unpacker = Unpack<GLushort, 1, 65535, 0>::unpack_l; + break; + case GL_INT: + _unpacker = Unpack<GLint, 2, 4294967295U, 1>::unpack_l; + break; + case GL_UNSIGNED_INT: + _unpacker = Unpack<GLuint, 1, 4294967295U, 0>::unpack_l; + break; + case GL_FLOAT: + _unpacker = Unpack<GLfloat, 1, 1, 0>::unpack_l; + break; + default: + throw BadType(type()); + } + break; + case GL_LUMINANCE_ALPHA: + switch (type()) { + case GL_BYTE: + _unpacker = Unpack<GLbyte, 2, 255, 1>::unpack_la; + break; + case GL_UNSIGNED_BYTE: + _unpacker = Unpack<GLubyte, 1, 255, 0>::unpack_la; + break; + case GL_SHORT: + _unpacker = Unpack<GLshort, 2, 65535, 1>::unpack_la; + break; + case GL_UNSIGNED_SHORT: + _unpacker = Unpack<GLushort, 1, 65535, 0>::unpack_la; + break; + case GL_INT: + _unpacker = Unpack<GLint, 2, 4294967295U, 1>::unpack_la; + break; + case GL_UNSIGNED_INT: + _unpacker = Unpack<GLuint, 2, 4294967295U, 0>::unpack_la; + break; + case GL_FLOAT: + _unpacker = Unpack<GLfloat, 1, 1, 0>::unpack_la; + break; + default: + throw BadType(type()); + } + break; + case GL_RGB: + switch (type()) { + case GL_BYTE: + _unpacker = Unpack<GLbyte, 2, 255, 1>::unpack_rgb; + break; + case GL_UNSIGNED_BYTE: + _unpacker = Unpack<GLubyte, 1, 255, 0>::unpack_rgb; + break; + case GL_SHORT: + _unpacker = Unpack<GLshort, 2, 65535, 1>::unpack_rgb; + break; + case GL_UNSIGNED_SHORT: + _unpacker = Unpack<GLushort, 1, 65535, 0>::unpack_rgb; + break; + case GL_INT: + _unpacker = Unpack<GLint, 2, 4294967295U, 1>::unpack_rgb; + break; + case GL_UNSIGNED_INT: + _unpacker = Unpack<GLuint, 1, 4294967295U, 0>::unpack_rgb; + break; + case GL_FLOAT: + _unpacker = Unpack<GLfloat, 1, 1, 0>::unpack_rgb; + break; + default: + throw BadType(type()); + } + break; + case GL_RGBA: + switch (type()) { + case GL_BYTE: + _unpacker = Unpack<GLbyte, 2, 255, 1>::unpack_rgba; + break; + case GL_UNSIGNED_BYTE: + _unpacker = Unpack<GLubyte, 1, 255, 0>::unpack_rgba; + break; + case GL_SHORT: + _unpacker = Unpack<GLshort, 2, 65535, 1>::unpack_rgba; + break; + case GL_UNSIGNED_SHORT: + _unpacker = Unpack<GLushort, 1, 65535, 0>::unpack_rgba; + break; + case GL_INT: + _unpacker = Unpack<GLint, 2, 4294967295U, 1>::unpack_rgba; + break; + case GL_UNSIGNED_INT: + _unpacker = Unpack<GLuint, 1, 4294967295U, 0>::unpack_rgba; + break; + case GL_FLOAT: + _unpacker = Unpack<GLfloat, 1, 1, 0>::unpack_rgba; + break; + default: + throw BadType(type()); + } + break; + default: + throw BadFormat(format()); + } + + validate(vbUnpacker); + return _unpacker; +} + +}; // namespace GLEAN diff --git a/src/libs/image/wrtiff.cpp b/src/libs/image/wrtiff.cpp new file mode 100644 index 0000000..f4b95ba --- /dev/null +++ b/src/libs/image/wrtiff.cpp @@ -0,0 +1,134 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// Implementation of image data, attribute, and I/O + +#include "image.h" +#include "tiffio.h" + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// writeTIFF - write image to TIFF file +/////////////////////////////////////////////////////////////////////////////// +void +Image::writeTIFF(const char* filename) { + static uint16 unassocAlpha[] = {EXTRASAMPLE_UNASSALPHA}; + GLsizei rowStep = rowSizeInBytes(); + + TIFF* tf = TIFFOpen(filename, "w"); + if (!tf) + throw CantOpen(filename); + + TIFFSetField(tf, TIFFTAG_IMAGELENGTH, height()); + TIFFSetField(tf, TIFFTAG_IMAGEWIDTH, width()); + TIFFSetField(tf, TIFFTAG_XRESOLUTION, 100.0); + TIFFSetField(tf, TIFFTAG_YRESOLUTION, 100.0); + TIFFSetField(tf, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); + TIFFSetField(tf, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(tf, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tf, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + // LZW would have been acceptable, were it not for patent + // issues. + TIFFSetField(tf, TIFFTAG_ROWSPERSTRIP, height()); + + switch (format()) { + case GL_LUMINANCE: + TIFFSetField(tf, TIFFTAG_SAMPLESPERPIXEL, 1); + TIFFSetField(tf, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + break; + case GL_LUMINANCE_ALPHA: + TIFFSetField(tf, TIFFTAG_SAMPLESPERPIXEL, 2); + TIFFSetField(tf, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + TIFFSetField(tf, TIFFTAG_EXTRASAMPLES, 1, unassocAlpha); + break; + case GL_RGB: + TIFFSetField(tf, TIFFTAG_SAMPLESPERPIXEL, 3); + TIFFSetField(tf, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + break; + case GL_RGBA: + TIFFSetField(tf, TIFFTAG_SAMPLESPERPIXEL, 4); + TIFFSetField(tf, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + TIFFSetField(tf, TIFFTAG_EXTRASAMPLES, 1, unassocAlpha); + break; + default: + TIFFClose(tf); + throw BadFormat(format()); + } + + switch (type()) { + case GL_BYTE: + TIFFSetField(tf, TIFFTAG_BITSPERSAMPLE, 8); + TIFFSetField(tf, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT); + break; + case GL_UNSIGNED_BYTE: + TIFFSetField(tf, TIFFTAG_BITSPERSAMPLE, 8); + TIFFSetField(tf, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); + break; + case GL_SHORT: + TIFFSetField(tf, TIFFTAG_BITSPERSAMPLE, 16); + TIFFSetField(tf, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT); + break; + case GL_UNSIGNED_SHORT: + TIFFSetField(tf, TIFFTAG_BITSPERSAMPLE, 16); + TIFFSetField(tf, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); + break; + case GL_INT: + TIFFSetField(tf, TIFFTAG_BITSPERSAMPLE, 32); + TIFFSetField(tf, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT); + break; + case GL_UNSIGNED_INT: + TIFFSetField(tf, TIFFTAG_BITSPERSAMPLE, 32); + TIFFSetField(tf, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); + break; + case GL_FLOAT: + TIFFSetField(tf, TIFFTAG_BITSPERSAMPLE, 32); + TIFFSetField(tf, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP); + break; + default: + TIFFClose(tf); + throw BadType(type()); + } + + { + // Write rows in reverse order, so that the usual OpenGL + // orientation won't result in an upside-down image for + // naive TIFF readers: + char* row = pixels() + (height() - 1) * rowStep; + for (GLsizei r = 0; r < height(); ++r, row -= rowStep) + TIFFWriteScanline(tf, row, r, 0); + } + + TIFFClose(tf); +}; // Image::writeTIFF + + +}; // namespace GLEAN diff --git a/src/libs/lex/Makefile b/src/libs/lex/Makefile new file mode 100644 index 0000000..0a885ae --- /dev/null +++ b/src/libs/lex/Makefile @@ -0,0 +1,6 @@ +include $(GLEAN_ROOT)/make/common.mak + +TARGET=liblex.a +HTARGET=lex.h + +include $(GLEAN_ROOT)/make/lib.mak diff --git a/src/libs/lex/lex.cpp b/src/libs/lex/lex.cpp new file mode 100644 index 0000000..84891f1 --- /dev/null +++ b/src/libs/lex/lex.cpp @@ -0,0 +1,167 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// lex.cpp: Implementation of simple lexical analyzer + +#include <ctype.h> +#include <stdlib.h> +#include "lex.h" + +namespace GLEAN { + + +/////////////////////////////////////////////////////////////////////////////// +// Constructor: +/////////////////////////////////////////////////////////////////////////////// +Lex::Lex(const char* s, bool ignoreCase/* = false */) { + input = s; + p = input; + ignoringCase = ignoreCase; +} // Lex::Lex + +/////////////////////////////////////////////////////////////////////////////// +// next - Fetch next token from the input string +/////////////////////////////////////////////////////////////////////////////// +void +Lex::next() { + while (isspace(*p)) + ++p; + + if (isalpha(*p) || *p == '_') { + id = ""; + if (ignoringCase) + while (isalnum(*p) || *p == '_') + id += tolower(*p++); + else + while (isalnum(*p) || *p == '_') + id += *p++; + token = ID; + return; + } + + if (isdigit(*p)) { + iValue = strtol(p, const_cast<char**>(&p), 0); + token = ICONST; + return; + } + + char nextC = 0; + char c = *p++; + if (c) + nextC = *p; + switch (c) { + case '|': + if (nextC == '|') { + ++p; + token = OR_OR; + } + else + token = OR; + break; + case '&': + if (nextC == '&') { + ++p; + token = AND_AND; + } + else + token = AND; + break; + case '<': + if (nextC == '=') { + ++p; + token = LE; + } + else + token = LT; + break; + case '>': + if (nextC == '=') { + ++p; + token = GE; + } + else + token = GT; + break; + case '=': + if (nextC == '=') { + ++p; + token = EQ; + } + else + token = ASSIGN; + break; + case '!': + if (nextC == '=') { + ++p; + token = NE; + } + else + token = BANG; + break; + case '+': + token = PLUS; + break; + case '-': + token = MINUS; + break; + case '*': + token = STAR; + break; + case '/': + token = SLASH; + break; + case '%': + token = PERCENT; + break; + case ',': + token = COMMA; + break; + case '(': + token = LPAREN; + break; + case ')': + token = RPAREN; + break; + case '.': + token = DOT; + break; + case '\0': + token = END; + --p; // push back '\0' so that token will always be END + break; + default: + throw Lexical("unrecognized symbol", p - input); + } + + return; +} // Lex::next + +} // namespace GLEAN diff --git a/src/libs/lex/lex.h b/src/libs/lex/lex.h new file mode 100644 index 0000000..87e24df --- /dev/null +++ b/src/libs/lex/lex.h @@ -0,0 +1,128 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// lex.h: Simple lexical analysis utilities + +// Given a string containing C-style tokens, returns information about +// successive tokens. Doesn't support all C tokens; just the few that +// GLEAN needs. + + +#ifndef __lex_h__ +#define __lex_h__ + +#include <string> + +#ifdef __WIN__ +// ERROR is already defined in some windows header file +#undef ERROR +#endif + +namespace GLEAN { + +class Lex { + protected: + + // State information: + const char* input; + const char* p; + bool ignoringCase; + + public: + + // Constructors: + + Lex(const char* s, bool ignoreCase = false); + // Creates a lexer which will draw input from the given string. + + // Exceptions: + + struct Error { }; // Base class for errors. + struct Lexical: public Error { // Lexical error in string. + const char* err; + int position; + Lexical(const char* e, int p) { + err = e; + position = p; + } + }; + struct InternalError: public Error { // Shouldn't happen. + }; + + // Tokens: + + typedef enum { + ERROR = 0, // erroneous token; must be zero + END, // end of input + + AND, // & + AND_AND, // && + ASSIGN, // = + BANG, // ! + COMMA, // , + DOT, // . + EQ, // == + GE, // >= + GT, // > + LE, // <= + LPAREN, // ( + LT, // < + MINUS, // - + NE, // != + OR, // | + OR_OR, // || + PERCENT, // % + PLUS, // + + RPAREN, // ) + SLASH, // / + STAR, // * + + ICONST, // signed integer constant + + ID // identifier + } Token; + + // Utilities: + + void next(); // fetch next token + + Token token; // current token + std::string id; // most recent identifier + int iValue; // most recent signed integer value + + inline int position() { // current position in input string + return p - input; + } +}; // class Lex + +} // namespace GLEAN + +#endif // __lex_h__ diff --git a/src/libs/lex/makefile.win b/src/libs/lex/makefile.win new file mode 100644 index 0000000..09183c3 --- /dev/null +++ b/src/libs/lex/makefile.win @@ -0,0 +1,129 @@ +.SILENT : + +!IF "$(CFG)" == "" +CFG=release +!ENDIF + +!IF "$(CFG)" != "release" && "$(CFG)" != "debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "makefile.win" CFG=release +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "release" +!MESSAGE "debug" +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!INCLUDE $(GLEAN_ROOT)\make\common.win + +FTARGET=lex +TARGET=$(FTARGET).lib + +LIB32_OBJS= \ + "$(INTDIR)\lex.obj" + +!IF "$(CFG)" == "release" + +DEFINES=$(DEFINES) /D "NDEBUG" + +OUTDIR=.\Release +INTDIR=.\Release + +ALL : "$(GLEAN_LIB_DIR)\$(TARGET)" + +CLEAN : + -@erase "$(INTDIR)\*.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(OUTDIR)\$(FTARGET).pch + -@deltree /y "$(OUTDIR)" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /ML /W3 /GX /O2 $(DEFINES) $(INCLUDE_DIRS) /Fp"$(INTDIR)\$(FTARGET).pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +LIB32=link.exe -lib +LIB32_FLAGS=/nologo /out:"$(GLEAN_LIB_DIR)\$(TARGET)" + +"$(GLEAN_LIB_DIR)\$(TARGET)" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +!ELSEIF "$(CFG)" == "debug" + +DEFINES=$(DEFINES) /D "_DEBUG" + +OUTDIR=.\Debug +INTDIR=.\Debug + +ALL : "$(GLEAN_LIB_DIR)\$(TARGET)" + + +CLEAN : + -@erase "$(INTDIR)\*.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(INTDIR)\vc60.pdb" + -@erase "$(INTDIR)\$(FTARGET).pch" + -@deltree /Y $(INTDIR) + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MLd /W3 /Gm /GX /ZI /Od $(DEFINES) $(INCLUDE_DIRS) /Fp"$(INTDIR)\$(FTARGET).pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +LIB32=link.exe -lib +LIB32_FLAGS=/nologo /out:"$(GLEAN_LIB_DIR)\$(TARGET)" + +"$(GLEAN_LIB_DIR)\$(TARGET)" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +!ENDIF + +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +ALL : $(DS_POSTBUILD_DEP) + +$(DS_POSTBUILD_DEP) : "$(GLEAN_LIB_DIR)\$(TARGET)" + echo Copying Header Files + copy .\lex.h $(GLEAN_INC_DIR) + + + + + diff --git a/src/libs/makefile.win b/src/libs/makefile.win new file mode 100644 index 0000000..8fa0dfd --- /dev/null +++ b/src/libs/makefile.win @@ -0,0 +1,78 @@ +.SILENT : + +!IF "$(CFG)" == "" +CFG=release +!ENDIF + +!IF "$(CFG)" != "release" && "$(CFG)" != "debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "makefile.win" CFG=release +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "release" +!MESSAGE "debug" +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +all : + cd rand + $(MAKE) /nologo /f makefile.win CFG=$(CFG) + cd .. + + cd lex + $(MAKE) /nologo /f makefile.win CFG=$(CFG) + cd .. + + cd stats + $(MAKE) /nologo /f makefile.win CFG=$(CFG) + cd .. + + cd dsurf + $(MAKE) /nologo /f makefile.win CFG=$(CFG) + cd .. + + cd image + $(MAKE) /nologo /f makefile.win CFG=$(CFG) + cd .. + + cd timer + $(MAKE) /nologo /f makefile.win CFG=$(CFG) + cd .. + +clean : + cd dsurf + $(MAKE) /nologo /f makefile.win CFG=$(CFG) clean + cd .. + + cd image + $(MAKE) /nologo /f makefile.win CFG=$(CFG) clean + cd .. + + cd lex + $(MAKE) /nologo /f makefile.win CFG=$(CFG) clean + cd .. + + cd rand + $(MAKE) /nologo /f makefile.win CFG=$(CFG) clean + cd .. + + cd stats + $(MAKE) /nologo /f makefile.win CFG=$(CFG) clean + cd .. + + cd timer + $(MAKE) /nologo /f makefile.win CFG=$(CFG) clean + cd .. + + + + + + + + diff --git a/src/libs/rand/Makefile b/src/libs/rand/Makefile new file mode 100644 index 0000000..25fa796 --- /dev/null +++ b/src/libs/rand/Makefile @@ -0,0 +1,5 @@ +include $(GLEAN_ROOT)/make/common.mak + +HTARGET=rand.h + +include $(GLEAN_ROOT)/make/h.mak diff --git a/src/libs/rand/makefile.win b/src/libs/rand/makefile.win new file mode 100644 index 0000000..2b949c7 --- /dev/null +++ b/src/libs/rand/makefile.win @@ -0,0 +1,12 @@ +.SILENT : + +!INCLUDE $(GLEAN_ROOT)\make\common.win + +ALL : + echo Copying Header Files + copy .\rand.h $(GLEAN_INC_DIR) + +CLEAN : + + + diff --git a/src/libs/rand/rand.h b/src/libs/rand/rand.h new file mode 100644 index 0000000..b47684d --- /dev/null +++ b/src/libs/rand/rand.h @@ -0,0 +1,125 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// rand.h: Simple random sequence generation utilities. + +// We provide these to eliminate dependencies on the operating +// system's random number generator. This makes it possible to +// compare results for a given graphics device running under different +// operating systems. + +// Based on Numerical Recipes, 2d ed., p. 284. + + +#ifndef __rand_h__ +#define __rand_h__ + + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// RandomBase: Quick-and-dirty linear congruential generator that serves as +// a base for other random-sequence classes. +/////////////////////////////////////////////////////////////////////////////// +class RandomBase { + unsigned int i; + public: + inline RandomBase(unsigned int seed): i(seed) { } + inline RandomBase(): i(1) { } + inline unsigned int next() { + i = 1664525 * i + 1013904223; + return i; + } +}; // class RandomBase + +/////////////////////////////////////////////////////////////////////////////// +// RandomBits: Returns a given number of random bits (expressed as an unsigned +// int, so the maximum portable number of bits is 32). +/////////////////////////////////////////////////////////////////////////////// +class RandomBits: public RandomBase { + unsigned int shift; + public: + inline RandomBits(unsigned int bits, unsigned int seed): + RandomBase(seed), shift(32 - bits) { } + inline RandomBits(unsigned int bits): + RandomBase(), shift(32 - bits) { } + inline unsigned int next() { return RandomBase::next() >> shift; } +}; // class RandomBits + +/////////////////////////////////////////////////////////////////////////////// +// RandomSignedBits: Returns a given number of random bits (expressed as a +// signed int, so the maximum portable number of bits is 32 including sign). +/////////////////////////////////////////////////////////////////////////////// +class RandomSignedBits: public RandomBase { + unsigned int shift; + public: + inline RandomSignedBits(unsigned int bits, unsigned int seed): + RandomBase(seed), shift(32 - bits) { } + inline RandomSignedBits(unsigned int bits): + RandomBase(), shift(32 - bits) { } + inline int next() { + return static_cast<int>(RandomBase::next()) >> shift; + } +}; // class RandomSignedBits + +/////////////////////////////////////////////////////////////////////////////// +// RandomDouble: Returns a random floating-point value in the closed +// interval [0.0, 1.0]. +/////////////////////////////////////////////////////////////////////////////// +class RandomDouble: public RandomBase { + public: + inline RandomDouble(unsigned int seed): RandomBase(seed) { } + inline RandomDouble(): RandomBase() { } + inline double next() { + return static_cast<double>(RandomBase::next()) / 4294967295.0; + } +}; // class RandomDouble + +/////////////////////////////////////////////////////////////////////////////// +// RandomBitsDouble: Returns a random floating-point value in the closed +// interval [0.0, 1.0], but with possible values limited by a generator +// returning a specific number of bits. +/////////////////////////////////////////////////////////////////////////////// +class RandomBitsDouble: public RandomBits { + double scale; + public: + inline RandomBitsDouble(unsigned int bits, unsigned int seed): + RandomBits(bits, seed) { scale = (1 << bits) - 1.0; } + inline RandomBitsDouble(unsigned int bits): + RandomBits(bits) { scale = (1 << bits) - 1.0; } + inline double next() { + return static_cast<double>(RandomBits::next()) / scale; + } +}; // class RandomBitsDouble + +} // namespace GLEAN + +#endif // __rand_h__ diff --git a/src/libs/stats/Makefile b/src/libs/stats/Makefile new file mode 100644 index 0000000..5b0d01d --- /dev/null +++ b/src/libs/stats/Makefile @@ -0,0 +1,6 @@ +include $(GLEAN_ROOT)/make/common.mak + +TARGET=libstats.a +HTARGET=stats.h + +include $(GLEAN_ROOT)/make/lib.mak diff --git a/src/libs/stats/basic.cpp b/src/libs/stats/basic.cpp new file mode 100644 index 0000000..af317bd --- /dev/null +++ b/src/libs/stats/basic.cpp @@ -0,0 +1,64 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// basic.cpp: basic statistics utilities for glean + +#include <cfloat> +#include <cmath> +#include "stats.h" + +namespace GLEAN { + +void +BasicStats::init() { + _min = DBL_MAX; + _max = -DBL_MAX; + _sum = _sum2 = 0.0; + _n = 0; +} + +double +BasicStats::mean() const {return _n? (_sum / _n): 0.0;} + +double +BasicStats::variance() const { + if (_n < 2) + return 0.0; + return (_sum2 - _sum * _sum / _n) / (_n - 1); + // Not really numerically robust, but good enough for us. +} +double +BasicStats::deviation() const { + const double v = variance(); + return (v < 0.0)? 0.0: sqrt(v); +} + +} // namespace GLEAN diff --git a/src/libs/stats/makefile.win b/src/libs/stats/makefile.win new file mode 100644 index 0000000..6e19e49 --- /dev/null +++ b/src/libs/stats/makefile.win @@ -0,0 +1,129 @@ +.SILENT : + +!IF "$(CFG)" == "" +CFG=release +!ENDIF + +!IF "$(CFG)" != "release" && "$(CFG)" != "debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "makefile.win" CFG=release +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "release" +!MESSAGE "debug" +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!INCLUDE $(GLEAN_ROOT)\make\common.win + +FTARGET=stats +TARGET=$(FTARGET).lib + +LIB32_OBJS= \ + "$(INTDIR)\basic.obj" + +!IF "$(CFG)" == "release" + +DEFINES=$(DEFINES) /D "NDEBUG" + +OUTDIR=.\Release +INTDIR=.\Release + +ALL : "$(GLEAN_LIB_DIR)\$(TARGET)" + +CLEAN : + -@erase "$(INTDIR)\*.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(OUTDIR)\$(FTARGET).pch + -@deltree /y "$(OUTDIR)" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /ML /W3 /GX /O2 $(DEFINES) $(INCLUDE_DIRS) /Fp"$(INTDIR)\$(FTARGET).pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +LIB32=link.exe -lib +LIB32_FLAGS=/nologo /out:"$(GLEAN_LIB_DIR)\$(TARGET)" + +"$(GLEAN_LIB_DIR)\$(TARGET)" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +!ELSEIF "$(CFG)" == "debug" + +DEFINES=$(DEFINES) /D "_DEBUG" + +OUTDIR=.\Debug +INTDIR=.\Debug + +ALL : "$(GLEAN_LIB_DIR)\$(TARGET)" + + +CLEAN : + -@erase "$(INTDIR)\*.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(INTDIR)\vc60.pdb" + -@erase "$(INTDIR)\$(FTARGET).pch" + -@deltree /Y $(INTDIR) + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MLd /W3 /Gm /GX /ZI /Od $(DEFINES) $(INCLUDE_DIRS) /Fp"$(INTDIR)\$(FTARGET).pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +LIB32=link.exe -lib +LIB32_FLAGS=/nologo /out:"$(GLEAN_LIB_DIR)\$(TARGET)" + +"$(GLEAN_LIB_DIR)\$(TARGET)" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +!ENDIF + +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +ALL : $(DS_POSTBUILD_DEP) + +$(DS_POSTBUILD_DEP) : "$(GLEAN_LIB_DIR)\$(TARGET)" + echo Copying Header Files + copy .\stats.h $(GLEAN_INC_DIR) + + + + + diff --git a/src/libs/stats/stats.h b/src/libs/stats/stats.h new file mode 100644 index 0000000..e2f6d30 --- /dev/null +++ b/src/libs/stats/stats.h @@ -0,0 +1,87 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// stats.h: simple statistics-gathering utilities for glean + +// These are rather simplistic. For more robust implementations, consider +// using Numerical Recipes. + +#ifndef __stats_h__ +#define __stats_h__ + +#include <vector> + +#if defined( __WIN__ ) + +#undef min +#undef max + +#endif + +namespace GLEAN { + +class BasicStats { + int _n; + double _min; + double _max; + double _sum; + double _sum2; + public: + void init(); + inline int n() const {return _n;} + inline double min() const {return _min;} + inline double max() const {return _max;} + inline double sum() const {return _sum;} + inline double sum2() const {return _sum2;} + double mean() const; + double variance() const; + double deviation() const; + inline void sample(double d) { + ++_n; + if (d < _min) + _min = d; + if (d > _max) + _max = d; + _sum += d; + _sum2 += d*d; + } + + BasicStats() {init();} + template<class T> BasicStats(std::vector<T>& v) { + init(); + for (std::vector<T>::const_iterator p = v.begin(); p < v.end(); ++p) + sample(*p); + } +}; // class BasicStats + +} // namespace GLEAN + +#endif // __stats_h__ diff --git a/src/libs/timer/Makefile b/src/libs/timer/Makefile new file mode 100644 index 0000000..dbd923e --- /dev/null +++ b/src/libs/timer/Makefile @@ -0,0 +1,6 @@ +include $(GLEAN_ROOT)/make/common.mak + +TARGET=libtimer.a +HTARGET=timer.h + +include $(GLEAN_ROOT)/make/lib.mak diff --git a/src/libs/timer/makefile.win b/src/libs/timer/makefile.win new file mode 100644 index 0000000..60c053f --- /dev/null +++ b/src/libs/timer/makefile.win @@ -0,0 +1,129 @@ +.SILENT : + +!IF "$(CFG)" == "" +CFG=release +!ENDIF + +!IF "$(CFG)" != "release" && "$(CFG)" != "debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "makefile.win" CFG=release +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "release" +!MESSAGE "debug" +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!INCLUDE $(GLEAN_ROOT)\make\common.win + +FTARGET=timer +TARGET=$(FTARGET).lib + +LIB32_OBJS= \ + "$(INTDIR)\timer.obj" + +!IF "$(CFG)" == "release" + +DEFINES=$(DEFINES) /D "NDEBUG" + +OUTDIR=.\Release +INTDIR=.\Release + +ALL : "$(GLEAN_LIB_DIR)\$(TARGET)" + +CLEAN : + -@erase "$(INTDIR)\*.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(OUTDIR)\$(FTARGET).pch + -@deltree /y "$(OUTDIR)" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /ML /W3 /GX /O2 $(DEFINES) $(INCLUDE_DIRS) /Fp"$(INTDIR)\$(FTARGET).pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +LIB32=link.exe -lib +LIB32_FLAGS=/nologo /out:"$(GLEAN_LIB_DIR)\$(TARGET)" + +"$(GLEAN_LIB_DIR)\$(TARGET)" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +!ELSEIF "$(CFG)" == "debug" + +DEFINES=$(DEFINES) /D "_DEBUG" + +OUTDIR=.\Debug +INTDIR=.\Debug + +ALL : "$(GLEAN_LIB_DIR)\$(TARGET)" + + +CLEAN : + -@erase "$(INTDIR)\*.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(INTDIR)\vc60.pdb" + -@erase "$(INTDIR)\$(FTARGET).pch" + -@deltree /Y $(INTDIR) + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MLd /W3 /Gm /GX /ZI /Od $(DEFINES) $(INCLUDE_DIRS) /Fp"$(INTDIR)\$(FTARGET).pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +LIB32=link.exe -lib +LIB32_FLAGS=/nologo /out:"$(GLEAN_LIB_DIR)\$(TARGET)" + +"$(GLEAN_LIB_DIR)\$(TARGET)" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +!ENDIF + +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +ALL : $(DS_POSTBUILD_DEP) + +$(DS_POSTBUILD_DEP) : "$(GLEAN_LIB_DIR)\$(TARGET)" + echo Copying Header Files + copy .\timer.h $(GLEAN_INC_DIR) + + + + + diff --git a/src/libs/timer/timer.cpp b/src/libs/timer/timer.cpp new file mode 100644 index 0000000..d8cff61 --- /dev/null +++ b/src/libs/timer/timer.cpp @@ -0,0 +1,213 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// timer.cpp: Implementation of simple benchmark timer utilities. + +// This particular implementation is derived from the one in libpdb, part +// of the isfast library for OpenGL. + +// XXXWIN as of 5/8/99: The code for Windows timing is taken from +// Michael Gold's implementation of libpdb. I've probably introduced +// some bugs in the translation, unfortunately. + +#include "timer.h" + +#if defined(__UNIX__) +# include <sys/time.h> // for gettimeofday, used by getClock +#elif defined(__MS__) +# include <windows.h> +# include <sys/types.h> +# include <sys/timeb.h> // for _ftime(), used by getClock +#endif + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// calibrate: Determine overhead of measurement, initialization routine, +// and finalization routine +/////////////////////////////////////////////////////////////////////////////// +void +Timer::calibrate(void (*initialize)(), void (*finalize)()) { + double runTime = chooseRunTime(); + + if (initialize) + (*initialize)(); + + long reps = 0; + double current; + double start = waitForTick(); + do { + if (finalize) + (*finalize)(); + ++reps; + } while ((current = getClock()) < start + runTime); + + overhead = (current - start) / (double) reps; +} // Timer::calibrate + +/////////////////////////////////////////////////////////////////////////////// +// chooseRunTime: Select an appropriate runtime for benchmarks. +// By running for at least 10000 ticks, and attempting to keep timing +// accurate to one tick, we hope to make our results repeatable. +// (ignoring all the other stuff that might be going on in the system, +// of course). Long runs reduce the effect of measurement error, but +// short runs reduce the chance that some other process on the system +// will steal time. +/////////////////////////////////////////////////////////////////////////////// +double +Timer::chooseRunTime() { + double start = getClock(); + double finish; + + // Wait for next tick: + while ((finish = getClock()) == start) + ; + + // Run for 10000 ticks, clamped to [0.1 sec, 5.0 sec]: + double runTime = 10000.0 * (finish - start); + if (runTime < 0.1) + runTime = 0.1; + else if (runTime > 5.0) + runTime = 5.0; + + return runTime; +} + +/////////////////////////////////////////////////////////////////////////////// +// getClock - get current wall-clock time (expressed in seconds) +/////////////////////////////////////////////////////////////////////////////// +double +Timer::getClock() { +#if defined(__MS__) + static int once = 1; + static double freq; + + if (once) { + LARGE_INTEGER fr; + freq = (double) (QueryPerformanceFrequency(&fr) ? + 1.0 / fr.QuadPart : 0); + once = 0; + } + + // Use high-resolution counter, if available + if (freq) { + LARGE_INTEGER pc; + QueryPerformanceCounter(&pc); + return freq * (double) pc.QuadPart; + } else { + struct _timeb t; + + _ftime(&t); + + return (double) t.time + (double) t.millitm * 1E-3; + } +#elif defined(__UNIX__) + struct timeval t; + + // XXX gettimeofday is different on SysV, if I remember correctly + gettimeofday(&t, 0); + + return (double) t.tv_sec + (double) t.tv_usec * 1E-6; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +// waitForTick: wait for beginning of next system clock tick; return the time. +/////////////////////////////////////////////////////////////////////////////// +double +Timer::waitForTick() { + double start; + double current; + + start = getClock(); + + // Wait for next tick: + while ((current = getClock()) == start) + ; + + // Start timing: + return current; +} + +/////////////////////////////////////////////////////////////////////////////// +// time: measure time (in seconds) to perform caller's operation +/////////////////////////////////////////////////////////////////////////////// +double +Timer::time( + void (*initialize)(), + void (*operation)(), + void (*finalize)()) { + + if (!operation) + return 0.0; + + // Select a run time that's appropriate for our timer resolution: + double runTime = chooseRunTime(); + + // Measure successively larger batches of operations until we find + // one that's long enough to meet our runtime target: + long reps = 1; + double start; + double current; + for (;;) { + if (initialize) + (*initialize)(); + + start = waitForTick(); + + for (long i = reps; i > 0; --i) + (*operation)(); + + if (finalize) + (*finalize)(); + + current = getClock(); + if (current >= start + runTime + overhead) + break; + + // Try to reach runtime target in one fell swoop: + long newReps; + if (current > start + overhead) + newReps = static_cast<long> (reps * + (0.5 + runTime / (current - start - overhead))); + else + newReps = reps * 2; + if (newReps == reps) + reps += 1; + else + reps = newReps; + } + + // Subtract overhead to determine the final operation rate: + return (current - start - overhead) / reps; +} // Timer::time + +} // namespace GLEAN diff --git a/src/libs/timer/timer.h b/src/libs/timer/timer.h new file mode 100644 index 0000000..cc7e12a --- /dev/null +++ b/src/libs/timer/timer.h @@ -0,0 +1,71 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +// timer.h: Simple benchmark timing utilities + +// Timer objects provide a framework for measuring the rate at which an +// operation can be performed. + + +#ifndef __timer_h__ +#define __timer_h__ + + +namespace GLEAN { + +class Timer { + double overhead; // Overhead (in seconds) of initial op, + // final op, and timer access. + + double chooseRunTime(); // Select a runtime that will reduce random + // timing error to an acceptable level. + + public: + double getClock(); // Get high resolution wall-clock time, in secs. + + double waitForTick(); // Wait for next clock tick, and return time. + + void calibrate( // Determine measurement overhead. + void (*initialize)(), + void (*finalize)()); + + double time( // Measure rate for a given operation. + void (*initialize)(), + void (*operation)(), + void (*finalize)()); + + + Timer() {overhead = 0.0;} +}; // class Timer + +} // namespace GLEAN + +#endif // __timer_h__ diff --git a/src/makefile.win b/src/makefile.win new file mode 100644 index 0000000..20e2ca2 --- /dev/null +++ b/src/makefile.win @@ -0,0 +1,71 @@ +.SILENT : + +!IF "$(CFG)" == "" +CFG=release +!ENDIF + +!IF "$(CFG)" != "release" && "$(CFG)" != "debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "makefile.win" CFG=release +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "release" +!MESSAGE "debug" +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +all : + if not exist "$(GLEAN_BIN_DIR)/$(NULL)" mkdir "$(GLEAN_BIN_DIR)" + if not exist "$(GLEAN_LIB_DIR)/$(NULL)" mkdir "$(GLEAN_LIB_DIR)" + if not exist "$(GLEAN_INC_DIR)/$(NULL)" mkdir "$(GLEAN_INC_DIR)" + + cd glh + $(MAKE) /nologo /f makefile.win CFG=$(CFG) + cd .. + + cd libs + $(MAKE) /nologo /f makefile.win CFG=$(CFG) + cd .. + + cd tools + $(MAKE) /nologo /f makefile.win CFG=$(CFG) + cd .. + + cd glean + $(MAKE) /nologo /f makefile.win CFG=$(CFG) + cd .. + +clean : + cd glh + $(MAKE) /nologo /f makefile.win CFG=$(CFG) clean + cd .. + + cd libs + $(MAKE) /nologo /f makefile.win CFG=$(CFG) clean + cd .. + + cd tools + $(MAKE) /nologo /f makefile.win CFG=$(CFG) clean + cd .. + + cd glean + $(MAKE) /nologo /f makefile.win CFG=$(CFG) clean + cd .. + + + + + + + diff --git a/src/tools/Makefile b/src/tools/Makefile new file mode 100644 index 0000000..7d8cc0a --- /dev/null +++ b/src/tools/Makefile @@ -0,0 +1,5 @@ +include $(GLEAN_ROOT)/make/common.mak + +DIRS=showtiff difftiff showvis + +include $(GLEAN_ROOT)/make/null.mak diff --git a/src/tools/difftiff/Makefile b/src/tools/difftiff/Makefile new file mode 100644 index 0000000..29d17c9 --- /dev/null +++ b/src/tools/difftiff/Makefile @@ -0,0 +1,6 @@ +include $(GLEAN_ROOT)/make/common.mak + +TARGET=difftiff +LIB=-limage -ltiff -lglut -lGLU -lGL -lXmu -lXext -lXi -lX11 + +include $(GLEAN_ROOT)/make/app.mak diff --git a/src/tools/difftiff/main.cpp b/src/tools/difftiff/main.cpp new file mode 100644 index 0000000..a5c1e53 --- /dev/null +++ b/src/tools/difftiff/main.cpp @@ -0,0 +1,401 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +#include <stdlib.h> +#include <iostream> +#include <GL/glut.h> +#include <cmath> // for fabs +#include "image.h" + +using namespace std; + + +enum { + MENU_IMAGE1, + MENU_IMAGE2, + MENU_DIFF, + MENU_DIFF_ON_1, + MENU_DIFF_ON_2, + MENU_QUIT +}; + +enum { + SHOW_IMAGE1, + SHOW_IMAGE2, + SHOW_DIFF, + SHOW_DIFF_ON_1, + SHOW_DIFF_ON_2 +} Mode = SHOW_DIFF_ON_1; + +enum { + DIFF_COLOR_MENU_WHITE, + DIFF_COLOR_MENU_BLACK, + DIFF_COLOR_MENU_RED, + DIFF_COLOR_MENU_GREEN, + DIFF_COLOR_MENU_BLUE, + DIFF_COLOR_MENU_YELLOW, + DIFF_COLOR_MENU_MAGENTA, + DIFF_COLOR_MENU_CYAN +}; + +void ComputeDifference(); +void DiffColorMenu(int item); +void Draw(); +void Keypress(unsigned char key, int x, int y); +void Menu(int item); +void Reshape(int width, int height); +void ScreenOrthoView(int w, int h); +void ThreshMenu(int item); + + +char* ApplicationName; +int WindowHeight; +int WindowWidth; +GLEAN::Image Image1; +GLEAN::Image Image2; +GLEAN::Image Diff; +double Threshold = 0.0 / 256.0; +unsigned char DiffColor[] = {255, 0, 255, 255}; + + +/////////////////////////////////////////////////////////////////////////////// +// ComputeDifference - generate the difference image +/////////////////////////////////////////////////////////////////////////////// +void +ComputeDifference() { + double* rgba1 = new double[4 * Image1.width()]; + char* row1 = Image1.pixels(); + double* rgba2 = new double[4 * Image2.width()]; + char* row2 = Image2.pixels(); + unsigned char* rowD = reinterpret_cast<unsigned char*>(Diff.pixels()); + + for (GLsizei i = 0; i < Diff.height(); ++i) { + Image1.unpack(Image1.width(), rgba1, row1); + Image2.unpack(Image2.width(), rgba2, row2); + + double* p1 = rgba1; + double* p2 = rgba2; + unsigned char* pD = rowD; + + for (GLsizei j = 0; j < Diff.width(); ++j) { + if (fabs(p1[0] - p2[0]) > Threshold + || fabs(p1[1] - p2[1]) > Threshold + || fabs(p1[2] - p2[2]) > Threshold + || fabs(p1[3] - p2[3]) > Threshold) { + pD[0] = DiffColor[0]; + pD[1] = DiffColor[1]; + pD[2] = DiffColor[2]; + pD[3] = DiffColor[3]; + } + else + pD[0] = pD[1] = pD[2] = pD[3] = 0; + + p1 += 4; + p2 += 4; + pD += 4; + } + + row1 += Image1.rowSizeInBytes(); + row2 += Image2.rowSizeInBytes(); + rowD += Diff.rowSizeInBytes(); + } + + delete[] rgba1; + delete[] rgba2; +} + +/////////////////////////////////////////////////////////////////////////////// +// DiffColorMenu - handle difference-color menu items +/////////////////////////////////////////////////////////////////////////////// +void +DiffColorMenu(int item) { + unsigned char c[3]; + + switch (item) { + case DIFF_COLOR_MENU_WHITE: + c[0] = c[1] = c[2] = 255; + break; + case DIFF_COLOR_MENU_BLACK: + c[0] = c[1] = c[2] = 0; + break; + case DIFF_COLOR_MENU_RED: + c[0] = 255; + c[1] = c[2] = 0; + break; + case DIFF_COLOR_MENU_GREEN: + c[0] = c[2] = 0; + c[1] = 255; + break; + case DIFF_COLOR_MENU_BLUE: + c[0] = c[1] = 0; + c[2] = 255; + break; + case DIFF_COLOR_MENU_YELLOW: + c[0] = c[1] = 255; + c[2] = 0; + break; + case DIFF_COLOR_MENU_MAGENTA: + c[0] = c[2] = 255; + c[1] = 0; + break; + case DIFF_COLOR_MENU_CYAN: + c[0] = 0; + c[1] = c[2] = 255; + break; + } + + if (c[0]!=DiffColor[0] || c[1]!=DiffColor[1] || c[2]!=DiffColor[2]) { + DiffColor[0] = c[0]; + DiffColor[1] = c[1]; + DiffColor[2] = c[2]; + ComputeDifference(); + glutPostRedisplay(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Draw - redraw the scene +/////////////////////////////////////////////////////////////////////////////// +void +Draw() { + glClear(GL_COLOR_BUFFER_BIT); + glRasterPos2f(1, 1); + switch (Mode) { + case SHOW_IMAGE1: + Image1.draw(); + break; + case SHOW_IMAGE2: + Image2.draw(); + break; + case SHOW_DIFF: + Diff.draw(); + break; + case SHOW_DIFF_ON_1: + Image1.draw(); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + Diff.draw(); + glDisable(GL_BLEND); + break; + case SHOW_DIFF_ON_2: + Image2.draw(); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + Diff.draw(); + glDisable(GL_BLEND); + break; + } + glutSwapBuffers(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Keypress - handle events from the keyboard +/////////////////////////////////////////////////////////////////////////////// +void +Keypress(unsigned char key, int x, int y) { + static_cast<void>(x); static_cast<void>(y); + switch (key) { + case 0x1b /*ESC*/: + case 'q': + case 'Q': + exit(0); + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// main - initialization and callback registration +/////////////////////////////////////////////////////////////////////////////// +int +main(int argc, char* argv[]) { + ApplicationName = argv[0]; + + if (argc != 3) { + cerr << "usage: " << ApplicationName + << " tiff-file1 tiff-file2\n"; + exit(1); + } + + try { + Image1.readTIFF(argv[1]); + } + catch (GLEAN::Image::Error) { + cerr << "can't open or handle " << argv[1] << "\n"; + exit(2); + } + + try { + Image2.readTIFF(argv[2]); + } + catch (GLEAN::Image::Error) { + cerr << "can't open or handle " << argv[2] << "\n"; + exit(3); + } + + if (Image1.width() != Image2.width() + || Image1.height() != Image2.height()) { + cerr << "image dimensions don't match\n"; + exit(4); + } + + Diff.width(Image1.width()); + Diff.height(Image1.height()); + Diff.format(GL_RGBA); + Diff.type(GL_UNSIGNED_BYTE); + Diff.alignment(1); + Diff.reserve(); + ComputeDifference(); + + glutInitWindowSize(Image1.width() + 2, Image1.height() + 2); + glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE); + glutInit(&argc, argv); + + glutCreateWindow("difftiff"); + + int diffColorMenu = glutCreateMenu(DiffColorMenu); + glutAddMenuEntry("white", DIFF_COLOR_MENU_WHITE); + glutAddMenuEntry("black", DIFF_COLOR_MENU_BLACK); + glutAddMenuEntry("red", DIFF_COLOR_MENU_RED); + glutAddMenuEntry("green", DIFF_COLOR_MENU_GREEN); + glutAddMenuEntry("blue", DIFF_COLOR_MENU_BLUE); + glutAddMenuEntry("yellow", DIFF_COLOR_MENU_YELLOW); + glutAddMenuEntry("magenta", DIFF_COLOR_MENU_MAGENTA); + glutAddMenuEntry("cyan", DIFF_COLOR_MENU_CYAN); + + int threshMenu = glutCreateMenu(ThreshMenu); + glutAddMenuEntry("1/2", 2); + glutAddMenuEntry("1/4", 4); + glutAddMenuEntry("1/8", 8); + glutAddMenuEntry("1/16", 16); + glutAddMenuEntry("1/32", 32); + glutAddMenuEntry("1/64", 64); + glutAddMenuEntry("1/128", 128); + glutAddMenuEntry("1/256", 256); + glutAddMenuEntry("0", 0); + + glutCreateMenu(Menu); + glutAddMenuEntry("Image 1", MENU_IMAGE1); + glutAddMenuEntry("Image 2", MENU_IMAGE2); + glutAddMenuEntry("Difference", MENU_DIFF); + glutAddMenuEntry("Difference + Image 1", MENU_DIFF_ON_1); + glutAddMenuEntry("Difference + Image 2", MENU_DIFF_ON_2); + glutAddSubMenu("Threshold", threshMenu); + glutAddSubMenu("Difference Highlight Color", diffColorMenu); + glutAddMenuEntry("Quit", MENU_QUIT); + glutAttachMenu(GLUT_RIGHT_BUTTON); + + glutDisplayFunc(Draw); + glutKeyboardFunc(Keypress); + glutReshapeFunc(Reshape); + + glutMainLoop(); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// Menu - handle invocations of menu commands +/////////////////////////////////////////////////////////////////////////////// +void +Menu(int item) { + switch (item) { + case MENU_IMAGE1: + Mode = SHOW_IMAGE1; + glutPostRedisplay(); + break; + case MENU_IMAGE2: + Mode = SHOW_IMAGE2; + glutPostRedisplay(); + break; + case MENU_DIFF: + Mode = SHOW_DIFF; + glutPostRedisplay(); + break; + case MENU_DIFF_ON_1: + Mode = SHOW_DIFF_ON_1; + glutPostRedisplay(); + break; + case MENU_DIFF_ON_2: + Mode = SHOW_DIFF_ON_2; + glutPostRedisplay(); + break; + case MENU_QUIT: + exit(EXIT_SUCCESS); + break; + default: + cerr << "Internal error; bogus menu item\n"; + exit(EXIT_FAILURE); + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Reshape - handle window reshape requests +/////////////////////////////////////////////////////////////////////////////// +void +Reshape(int width, int height) { + WindowWidth = width; + WindowHeight = height; + glViewport(0, 0, width, height); + ScreenOrthoView(width, height); + glutPostRedisplay(); +} + +/////////////////////////////////////////////////////////////////////////////// +// ScreenOrthoView - simple parallel view that matches screen pixel coords +// (using the OpenGL convention for origin at lower left) +/////////////////////////////////////////////////////////////////////////////// +void +ScreenOrthoView(int w, int h) { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0,w, 0,h, -1,1); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.375, 0.375, 0.0); +} + +/////////////////////////////////////////////////////////////////////////////// +// ThreshMenu - handle threshold submenu items +/////////////////////////////////////////////////////////////////////////////// +void +ThreshMenu(int item) { + double newThreshold; + if (item == 0) + newThreshold = 0.0; + else + newThreshold = 1.0 / static_cast<double>(item); + if (newThreshold != Threshold) { + Threshold = newThreshold; + ComputeDifference(); + glutPostRedisplay(); + } +} diff --git a/src/tools/difftiff/makefile.win b/src/tools/difftiff/makefile.win new file mode 100644 index 0000000..f99a19a --- /dev/null +++ b/src/tools/difftiff/makefile.win @@ -0,0 +1,118 @@ +.SILENT : + +!IF "$(CFG)" == "" +CFG=release +#!MESSAGE No configuration specified. Defaulting to release build. +!ENDIF + +!IF "$(CFG)" != "release" && "$(CFG)" != "debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f makefile.win CFG="release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "release" +!MESSAGE "debug" +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!INCLUDE $(GLEAN_ROOT)\make\common.win + +LINK32_OBJS= "$(INTDIR)\main.obj" + +LIBS=image.lib libtiff.lib opengl32.lib glu32.lib glut32.lib kernel32.lib user32.lib gdi32.lib + +FTARGET=difftiff +TARGET=$(FTARGET).exe + +!IF "$(CFG)" == "release" + +DEFINES=$(DEFINES) /D "NDEBUG" + +OUTDIR=.\Release +INTDIR=.\Release + +ALL : "$(GLEAN_BIN_DIR)\$(TARGET)" + +CLEAN : + -@erase "$(INTDIR)\*.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(OUTDIR)\$(FTARGET).pch + -@deltree /Y "$(OUTDIR)" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /ML /W3 /GX /O2 $(DEFINES) $(INCLUDE_DIRS) /Fp"$(INTDIR)\$(FTARGET).pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +LINK32=link.exe +LINK32_FLAGS=$(LIBS) /nologo /subsystem:console /machine:I386 /include:"__imp__glGetString@4" /out:"$(GLEAN_BIN_DIR)\$(TARGET)" /nodefaultlib:libcd.lib + +"$(GLEAN_BIN_DIR)\$(TARGET)" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) $(LINK32_FLAGS) $(LINK32_OBJS) $(LIB_DIRS) + + +!ELSEIF "$(CFG)" == "debug" + +DEFINES=$(DEFINES) /D "_DEBUG" + +OUTDIR=.\Debug +INTDIR=.\Debug + +ALL : "$(GLEAN_BIN_DIR)\$(TARGET)" + +CLEAN : + -@erase "$(INTDIR)\*.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(INTDIR)\vc60.pdb" + -@erase "$(OUTDIR)\$(FTARGET).pdb" + -@erase "$(GLEAN_BIN_DIR)\$(FTARGET).ilk" + -@erase "$(OUTDIR)\$(FTARGET).pch + -@deltree /Y "$(OUTDIR)" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MLd /W3 /Gm /GX /ZI /Od $(DEFINES) $(INCLUDE_DIRS) /Fp"$(INTDIR)\$(FTARGET).pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +LINK32=link.exe +LINK32_FLAGS=$(LIBS) /nologo /subsystem:console /incremental:yes /pdb:"$(OUTDIR)\$(FTARGET).pdb" /debug /machine:I386 /include:"__imp__glGetString@4" /out:"$(GLEAN_BIN_DIR)\$(TARGET)" /pdbtype:sept $(LIB_DIRS) + +"$(GLEAN_BIN_DIR)\$(TARGET)" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) $(LINK32_FLAGS) $(LINK32_OBJS) + +!ENDIF + + diff --git a/src/tools/makefile.win b/src/tools/makefile.win new file mode 100644 index 0000000..d33e8de --- /dev/null +++ b/src/tools/makefile.win @@ -0,0 +1,54 @@ +.SILENT : + +!IF "$(CFG)" == "" +CFG=release +!ENDIF + +!IF "$(CFG)" != "release" && "$(CFG)" != "debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "makefile.win" CFG=release +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "release" +!MESSAGE "debug" +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +all : + cd difftiff + nmake /nologo /f makefile.win CFG=$(CFG) + cd .. + + cd showtiff + nmake /nologo /f makefile.win CFG=$(CFG) + cd .. + + cd showvis + nmake /nologo /f makefile.win CFG=$(CFG) + cd .. + +clean : + cd difftiff + nmake /nologo /f makefile.win CFG=$(CFG) clean + cd .. + + cd showtiff + nmake /nologo /f makefile.win CFG=$(CFG) clean + cd .. + + cd showvis + nmake /nologo /f makefile.win CFG=$(CFG) clean + cd .. + + + + + + + + diff --git a/src/tools/showtiff/Makefile b/src/tools/showtiff/Makefile new file mode 100644 index 0000000..44d189d --- /dev/null +++ b/src/tools/showtiff/Makefile @@ -0,0 +1,6 @@ +include $(GLEAN_ROOT)/make/common.mak + +TARGET=showtiff +LIB=-limage -ltiff -lglut -lGLU -lGL -lXmu -lXext -lXi -lX11 + +include $(GLEAN_ROOT)/make/app.mak diff --git a/src/tools/showtiff/main.cpp b/src/tools/showtiff/main.cpp new file mode 100644 index 0000000..fe3cbb6 --- /dev/null +++ b/src/tools/showtiff/main.cpp @@ -0,0 +1,163 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +#include <stdlib.h> +#include <iostream> +#include <GL/glut.h> +#include "image.h" + +using namespace std; + +#define MENU_QUIT 1 + +void Draw(void); +void Keypress(unsigned char key, int x, int y); +void Menu(int item); +void Reshape(int width, int height); +void ScreenOrthoView(int w, int h); + + +char* ApplicationName; +int WindowHeight; +int WindowWidth; +GLEAN::Image Image; + + +/////////////////////////////////////////////////////////////////////////////// +// Draw - redraw the scene +/////////////////////////////////////////////////////////////////////////////// +void +Draw(void) { + glClear(GL_COLOR_BUFFER_BIT); + glRasterPos2f(1, 1); + Image.draw(); + glutSwapBuffers(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Keypress - handle events from the keyboard +/////////////////////////////////////////////////////////////////////////////// +void +Keypress(unsigned char key, int x, int y) { + static_cast<void>(x); static_cast<void>(y); + switch (key) { + case 0x1b /*ESC*/: + case 'q': + case 'Q': + exit(0); + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// main - initialization and callback registration +/////////////////////////////////////////////////////////////////////////////// +int +main(int argc, char* argv[]) { + ApplicationName = argv[0]; + + if (argc != 2) { + cerr << "usage: " << ApplicationName << " tiff-file\n"; + exit(1); + } + + try { + Image.readTIFF(argv[1]); + } + catch (GLEAN::Image::CantOpen) { + cerr << "can't open " << argv[1] << "\n"; + exit(2); + } + catch (GLEAN::Image::Error) { + cerr << "can't handle " << argv[1] << "\n"; + exit(2); + } + + glutInitWindowSize(Image.width() + 2, Image.height() + 2); + glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE); + glutInit(&argc, argv); + + glutCreateWindow("showtiff"); + + glutCreateMenu(Menu); + glutAddMenuEntry("Quit", MENU_QUIT); + glutAttachMenu(GLUT_RIGHT_BUTTON); + + glutDisplayFunc(Draw); + glutKeyboardFunc(Keypress); + glutReshapeFunc(Reshape); + + glutMainLoop(); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// Menu - handle invocations of menu commands +/////////////////////////////////////////////////////////////////////////////// +void +Menu(int item) { + switch (item) { + case MENU_QUIT: + exit(EXIT_SUCCESS); + break; + default: + cerr << "Internal error; bogus menu item\n"; + exit(EXIT_FAILURE); + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Reshape - handle window reshape requests +/////////////////////////////////////////////////////////////////////////////// +void +Reshape(int width, int height) { + WindowWidth = width; + WindowHeight = height; + glViewport(0, 0, width, height); + ScreenOrthoView(width, height); + glutPostRedisplay(); +} + +/////////////////////////////////////////////////////////////////////////////// +// ScreenOrthoView - simple parallel view that matches screen pixel coords +// (using the OpenGL convention for origin at lower left) +/////////////////////////////////////////////////////////////////////////////// +void +ScreenOrthoView(int w, int h) { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0,w, 0,h, -1,1); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.375, 0.375, 0.0); +} diff --git a/src/tools/showtiff/makefile.win b/src/tools/showtiff/makefile.win new file mode 100644 index 0000000..3e84c2c --- /dev/null +++ b/src/tools/showtiff/makefile.win @@ -0,0 +1,118 @@ +.SILENT : + +!IF "$(CFG)" == "" +CFG=release +#!MESSAGE No configuration specified. Defaulting to release build. +!ENDIF + +!IF "$(CFG)" != "release" && "$(CFG)" != "debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f makefile.win CFG="release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "release" +!MESSAGE "debug" +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!INCLUDE $(GLEAN_ROOT)\make\common.win + +LINK32_OBJS= "$(INTDIR)\main.obj" + +LIBS=image.lib libtiff.lib opengl32.lib glu32.lib glut32.lib kernel32.lib user32.lib gdi32.lib + +FTARGET=showtiff +TARGET=$(FTARGET).exe + +!IF "$(CFG)" == "release" + +DEFINES=$(DEFINES) /D "NDEBUG" + +OUTDIR=.\Release +INTDIR=.\Release + +ALL : "$(GLEAN_BIN_DIR)\$(TARGET)" + +CLEAN : + -@erase "$(INTDIR)\*.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(OUTDIR)\$(FTARGET).pch + -@deltree /Y "$(OUTDIR)" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /ML /W3 /GX /O2 $(DEFINES) $(INCLUDE_DIRS) /Fp"$(INTDIR)\$(FTARGET).pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +LINK32=link.exe +LINK32_FLAGS=$(LIBS) /nologo /subsystem:console /machine:I386 /include:"__imp__glGetString@4" /out:"$(GLEAN_BIN_DIR)\$(TARGET)" /nodefaultlib:libcd.lib + +"$(GLEAN_BIN_DIR)\$(TARGET)" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) $(LINK32_FLAGS) $(LINK32_OBJS) $(LIB_DIRS) + + +!ELSEIF "$(CFG)" == "debug" + +DEFINES=$(DEFINES) /D "_DEBUG" + +OUTDIR=.\Debug +INTDIR=.\Debug + +ALL : "$(GLEAN_BIN_DIR)\$(TARGET)" + +CLEAN : + -@erase "$(INTDIR)\*.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(INTDIR)\vc60.pdb" + -@erase "$(OUTDIR)\$(FTARGET).pdb" + -@erase "$(GLEAN_BIN_DIR)\$(FTARGET).ilk" + -@erase "$(OUTDIR)\$(FTARGET).pch + -@deltree /Y "$(OUTDIR)" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MLd /W3 /Gm /GX /ZI /Od $(DEFINES) $(INCLUDE_DIRS) /Fp"$(INTDIR)\$(FTARGET).pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +LINK32=link.exe +LINK32_FLAGS=$(LIBS) /nologo /subsystem:console /incremental:yes /pdb:"$(OUTDIR)\$(FTARGET).pdb" /debug /machine:I386 /include:"__imp__glGetString@4" /out:"$(GLEAN_BIN_DIR)\$(TARGET)" /pdbtype:sept $(LIB_DIRS) + +"$(GLEAN_BIN_DIR)\$(TARGET)" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) $(LINK32_FLAGS) $(LINK32_OBJS) + +!ENDIF + + diff --git a/src/tools/showvis/Makefile b/src/tools/showvis/Makefile new file mode 100644 index 0000000..356b622 --- /dev/null +++ b/src/tools/showvis/Makefile @@ -0,0 +1,6 @@ +include $(GLEAN_ROOT)/make/common.mak + +TARGET=showvis +LIB=-ldsurf -llex -lGLU -lGL -lXext -lX11 + +include $(GLEAN_ROOT)/make/app.mak diff --git a/src/tools/showvis/main.cpp b/src/tools/showvis/main.cpp new file mode 100644 index 0000000..05bc761 --- /dev/null +++ b/src/tools/showvis/main.cpp @@ -0,0 +1,293 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + + + + +#include <stdlib.h> +#include <iostream> +#include <string> +#include "glwrap.h" +#include "dsconfig.h" +#include "dsfilt.h" + +char* mandatoryArg(int argc, char* argv[], int i); +void usage(char* appName); + +/////////////////////////////////////////////////////////////////////////////// +// showvis - display list of visuals (X11 Visuals, GLX FBConfigs, or +// Win32 PixelFormatDescriptors) matching user-supplied criteria +/////////////////////////////////////////////////////////////////////////////// +int +main(int argc, char* argv[]) { + string criteria; + bool canonical = false; + +# if defined(__X11__) + string displayName(":0"); +# endif + + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--help")) { + usage(argv[0]); + } else if (!strcmp(argv[i], "-c") + || !strcmp(argv[i], "--canonical")) { + canonical = true; +# if defined(__X11__) + } else if (!strcmp(argv[i], "-display")) { + ++i; + displayName = mandatoryArg(argc, argv, i); +# endif + } else { + // Concatenate all non-option arguments, separating + // them with commas, to form a single string of + // criteria: + if (criteria.size()) + criteria += ","; + criteria += argv[i]; + } + } + + // By default, display all visuals: + if (criteria.size() == 0) + criteria = "1"; + + +# if defined(__X11__) && !defined(GLX_VERSION_1_3) + Display* dpy = XOpenDisplay(displayName.c_str()); + + if (!dpy) { + cerr << "can't open display " << displayName << "\n"; + exit(2); + } + + // Get the list of raw XVisualInfo structures: + XVisualInfo t; + t.screen = DefaultScreen(dpy); + int n; + XVisualInfo* vi = + XGetVisualInfo(dpy, VisualScreenMask, &t, &n); + + // Construct a vector of DrawingSurfaceConfigs corresponding + // to the XVisualInfo structures that indicate they support + // OpenGL: + vector<GLEAN::DrawingSurfaceConfig*> glxv; + for (int i = 0; i < n; ++i) { + int supportsOpenGL; + glXGetConfig(dpy, &vi[i], GLX_USE_GL, &supportsOpenGL); + if (supportsOpenGL) + glxv.push_back(new GLEAN::DrawingSurfaceConfig + (dpy, &vi[i])); + } + + // Create a configuration filter and apply it: + try { + GLEAN::DrawingSurfaceFilter f(criteria); + vector<GLEAN::DrawingSurfaceConfig*> v(f.filter(glxv)); + + for(vector<GLEAN::DrawingSurfaceConfig*>::const_iterator + p = v.begin(); p < v.end(); ++p) + cout << (canonical? (*p)->canonicalDescription() + : (*p)->conciseDescription()) + << "\n"; + + exit (v.size() == 0); + } + catch (GLEAN::DrawingSurfaceFilter::Syntax e) { + cerr << "Syntax error:\n'" << criteria << "'\n"; + for (int i = 0; i < e.position; ++i) + cerr << ' '; + cerr << "^ " << e.err << "\n"; + exit(2); + } + + +# elif defined(__X11__) && defined(GLX_VERSION_1_3) +# error "XXX Need to write GLX 1.3 FBConfig code" + // Yeah, yeah, it shouldn't be hard. I just don't + // have any way to test it yet. +# elif defined(__WIN__) + HDC hDC = GetDC(GetDesktopWindow()); + + int n = DescribePixelFormat(hDC,0,sizeof(PIXELFORMATDESCRIPTOR),0); + + PIXELFORMATDESCRIPTOR *pfd = new PIXELFORMATDESCRIPTOR[n+1]; + + // Construct a vector of DrawingSurfaceConfigs corresponding + // to all available pixelformats + vector<GLEAN::DrawingSurfaceConfig*> glxv; + for (int j = 1;j <= n;j++) { + DescribePixelFormat(hDC,j,sizeof(PIXELFORMATDESCRIPTOR),&pfd[j]); + glxv.push_back(new GLEAN::DrawingSurfaceConfig(j,&pfd[j])); + } + + ReleaseDC(GetDesktopWindow(),hDC); + + // Create a configuration filter and apply it: + try { + GLEAN::DrawingSurfaceFilter f(criteria); + vector<GLEAN::DrawingSurfaceConfig*> v(f.filter(glxv)); + + for(vector<GLEAN::DrawingSurfaceConfig*>::const_iterator + p = v.begin(); p < v.end(); ++p) + cout << (canonical? (*p)->canonicalDescription() + : (*p)->conciseDescription()) + << "\n"; + + exit (v.size() == 0); + } + catch (GLEAN::DrawingSurfaceFilter::Syntax e) { + cerr << "Syntax error:\n'" << criteria << "'\n"; + for (int i = 0; i < e.position; ++i) + cerr << ' '; + cerr << "^ " << e.err << "\n"; + exit(2); + } + + return 0; + +# endif +} + +/////////////////////////////////////////////////////////////////////////////// +// mandatoryArg - fetch a required argument from the command line; +// if it isn't present, print a usage message and die. +/////////////////////////////////////////////////////////////////////////////// + +char* +mandatoryArg(int argc, char* argv[], int i) { + if (i < argc && argv[i][0] != '-') + return argv[i]; + usage(argv[0]); + /*NOTREACHED*/ + return 0; +} // mandatoryArg + +/////////////////////////////////////////////////////////////////////////////// +// usage - print usage message, then die +/////////////////////////////////////////////////////////////////////////////// +void +usage(char* appName) { + cerr << "Usage: " << appName << "[options] {selection-expression}\n" +"\n" +"options:\n" +#if defined(__X11__) +" -display X11-display-name # select X11 display to use\n" +#elif defined(__WIN__) +#endif +" --help # display usage information\n" +" (-c|--canonical) # display visuals in canonical form,\n" +" # rather than abbreviated form\n" +"\n" +"selection-expression:\n" +" Selection expressions are C expressions involving the usual\n" +" arithmetic and logical operators, plus variables that correspond\n" +" to attributes of OpenGL-capable visuals (pixel formats).\n" +" Operators are:\n" +" ! -(unary) + - * / % == != < <= > >= && || ( )\n" +" Variables are:\n" +" r red channel size\n" +" g green channel size\n" +" b blue channel size\n" +" a alpha channel size\n" +" rgb minimum of r, g, and b\n" +" rgba minimum of r, g, b, and a\n" +" ci color index channel size\n" +" accumr accumulation buffer red channel size\n" +" accumg accumulation buffer green channel size\n" +" accumb accumulation buffer blue channel size\n" +" accuma accumulation buffer alpha channel size\n" +" accumrgb minimum of accumr, accumg, and accumb\n" +" accumrgba minimum of accumr, accumg, accumb, and accuma\n" +" aux number of auxiliary color buffers\n" +" db nonzero if visual is double-buffered\n" +" sb nonzero if visual is single-buffered\n" +" id system-dependent ID number of visual\n" +" fbcid framebuffer configuration ID (GLX 1.3 only)\n" +" level <0 if underlay; ==0 if main; >0 if overlay\n" +" main nonzero if visual is in main planes\n" +" overlay nonzero if visual is in overlay planes\n" +" underlay nonzero if visual is in underlay planes\n" +" mono nonzero if visual is monoscopic\n" +" stereo nonzero if visual is stereoscopic\n" +" ms number of multisamples\n" +" s stencil buffer size\n" +" z depth (Z) buffer size\n" +" fast nonzero if visual is accelerated\n" +" conformant nonzero if visual conforms to OpenGL spec\n" +" transparent nonzero if visual has a transparent pixel value\n" +" transr transparent pixel red value (RGBA visuals only)\n" +" transg transparent pixel green value\n" +" transb transparent pixel blue value\n" +" transa transparent pixel alpha value\n" +" transci transparent pixel color index value (CI visuals\n" +" only)\n" +" window nonzero if visual can be used to create windows\n" +#if defined(__X11__) +" pixmap nonzero if visual can be used to create pixmaps\n" +#if defined(GLX_VERSION_1_3) +" pbuffer nonzero if visual can be used to create pbuffers\n" +#endif +#endif +" glonly nonzero if visual can be used only for OpenGL\n" +" rendering\n" +"\n" +" Selection expressions may also include sort keys, which consist of\n" +" ``max'' or ``min'' followed by a variable. When multiple visuals\n" +" meeting the selection criteria are found, those with the largest\n" +" (for max) or smallest (for min) value of the given variable are\n" +" listed first.\n" +"\n" +" Finally, multiple selection expressions may be provided as separate\n" +" arguments or separated by commas; in either case, only visuals that\n" +" meet all the selection expressions are returned.\n" +"\n" +" Exit status is 0 (normal) if visuals were found, 1 if no matching\n" +" visuals were found, and 2 if any error occurred\n" +"\n" +"Examples:\n" +" " << appName << "\n" +" display all OpenGL-capable visuals\n" +" " << appName << " db rgb\n" +" display all double-buffered RGB or RGBA visuals\n" +" " << appName << " 'r != g || g != b'\n" +" display all visuals for which the color channels are not all\n" +" the same size\n" +" " << appName << " 'max rgba, fast, conformant, accumrgba >= 2*rgba'\n" +" display all visuals that are accelerated and OpenGL\n" +" conformant, and for which the accumulation buffer is at\n" +" least twice as deep as the normal color buffer, sorted with\n" +" the deepest color buffer first\n" +" " << appName << " 'r%2 || g%2 || b%2 '\n" +" display all visuals for which at least one color channel\n" +" has an odd size\n" +" " << appName << " 'min ci'\n" +" display color index visuals, smallest first\n"; + + exit(2); +} diff --git a/src/tools/showvis/makefile.win b/src/tools/showvis/makefile.win new file mode 100644 index 0000000..d85d7ac --- /dev/null +++ b/src/tools/showvis/makefile.win @@ -0,0 +1,118 @@ +.SILENT : + +!IF "$(CFG)" == "" +CFG=release +#!MESSAGE No configuration specified. Defaulting to release build. +!ENDIF + +!IF "$(CFG)" != "release" && "$(CFG)" != "debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f makefile.win CFG="release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "release" +!MESSAGE "debug" +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!INCLUDE $(GLEAN_ROOT)\make\common.win + +LINK32_OBJS= "$(INTDIR)\main.obj" + +LIBS=lex.lib dsurf.lib opengl32.lib kernel32.lib user32.lib gdi32.lib + +FTARGET=showvis +TARGET=$(FTARGET).exe + +!IF "$(CFG)" == "release" + +DEFINES=$(DEFINES) /D "NDEBUG" + +OUTDIR=.\Release +INTDIR=.\Release + +ALL : "$(GLEAN_BIN_DIR)\$(TARGET)" + +CLEAN : + -@erase "$(INTDIR)\*.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(OUTDIR)\$(FTARGET).pch + -@deltree /Y "$(OUTDIR)" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /ML /W3 /GX /O2 $(DEFINES) $(INCLUDE_DIRS) /Fp"$(INTDIR)\$(FTARGET).pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +LINK32=link.exe +LINK32_FLAGS=$(LIBS) /nologo /subsystem:console /machine:I386 /include:"__imp__glGetString@4" /out:"$(GLEAN_BIN_DIR)\$(TARGET)" /nodefaultlib:libcd.lib + +"$(GLEAN_BIN_DIR)\$(TARGET)" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) $(LINK32_FLAGS) $(LINK32_OBJS) $(LIB_DIRS) + + +!ELSEIF "$(CFG)" == "debug" + +DEFINES=$(DEFINES) /D "_DEBUG" + +OUTDIR=.\Debug +INTDIR=.\Debug + +ALL : "$(GLEAN_BIN_DIR)\$(TARGET)" + +CLEAN : + -@erase "$(INTDIR)\*.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(INTDIR)\vc60.pdb" + -@erase "$(OUTDIR)\$(FTARGET).pdb" + -@erase "$(GLEAN_BIN_DIR)\$(FTARGET).ilk" + -@erase "$(OUTDIR)\$(FTARGET).pch + -@deltree /Y "$(OUTDIR)" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MLd /W3 /Gm /GX /ZI /Od $(DEFINES) $(INCLUDE_DIRS) /Fp"$(INTDIR)\$(FTARGET).pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +LINK32=link.exe +LINK32_FLAGS=$(LIBS) /nologo /subsystem:console /incremental:no /pdb:"$(OUTDIR)\$(FTARGET).pdb" /debug /machine:I386 /include:"__imp__glGetString@4" /out:"$(GLEAN_BIN_DIR)\$(TARGET)" /pdbtype:sept $(LIB_DIRS) + +"$(GLEAN_BIN_DIR)\showvis.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) $(LINK32_FLAGS) $(LINK32_OBJS) + +!ENDIF + + |