added png and image comparison, enabling testing on linux

stl_dim
Don Bright 2011-09-17 16:13:37 -05:00
parent 78bf15d085
commit 6d70855a4d
14 changed files with 8606 additions and 436 deletions

View File

@ -53,17 +53,6 @@ void export_stl(CGAL_Nef_polyhedron *root_N, std::ostream &output, QProgressDial
setlocale(LC_NUMERIC, "C"); // Ensure radix is . (not ,) in output
<<<<<<< HEAD
std::ofstream output(filename.toUtf8());
if (!output.is_open()) {
PRINTA("Can't open STL file \"%1\" for STL export: %2",
filename, QString(strerror(errno)));
set_output_handler(NULL, NULL);
return;
}
=======
>>>>>>> upstream/visitor
output << "solid OpenSCAD_Model\n";
int facet_count = 0;
@ -122,10 +111,6 @@ void export_stl(CGAL_Nef_polyhedron *root_N, std::ostream &output, QProgressDial
}
output << "endsolid OpenSCAD_Model\n";
<<<<<<< HEAD
output.close();
=======
>>>>>>> upstream/visitor
setlocale(LC_NUMERIC, ""); // Set default locale
}

View File

@ -317,15 +317,6 @@ int main(int argc, char **argv)
}
}
<<<<<<< HEAD
if (root_N.dim == 3 && !root_N.p3.is_simple()) {
fprintf(stderr, "Object isn't a valid 2-manifold! Modify your design.\n");
exit(1);
}
if (stl_output_file)
export_stl(&root_N, stl_output_file, NULL);
=======
if (stl_output_file) {
if (root_N.dim != 3) {
fprintf(stderr, "Current top level object is not a 3D object.\n");
@ -344,7 +335,6 @@ int main(int argc, char **argv)
fstream.close();
}
}
>>>>>>> upstream/visitor
if (off_output_file) {
if (root_N.dim != 3) {
@ -425,3 +415,4 @@ int main(int argc, char **argv)
return rc;
}

View File

@ -314,285 +314,5 @@ BoundingBox PolySet::getBoundingBox() const
bbox.extend(p);
}
}
<<<<<<< HEAD
};
CGAL_Nef_polyhedron PolySet::render_cgal_nef_polyhedron() const
{
if (this->is2d)
{
#if 0
// This version of the code causes problems in some cases.
// Example testcase: import_dxf("testdata/polygon8.dxf");
//
typedef std::list<CGAL_Nef_polyhedron2::Point> point_list_t;
typedef point_list_t::iterator point_list_it;
std::list< point_list_t > pdata_point_lists;
std::list < std::pair < point_list_it, point_list_it > > pdata;
Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
for (int i = 0; i < this->polygons.size(); i++) {
pdata_point_lists.push_back(point_list_t());
for (int j = 0; j < this->polygons[i].size(); j++) {
double x = this->polygons[i][j].x;
double y = this->polygons[i][j].y;
CGAL_Nef_polyhedron2::Point p;
if (grid.has(x, y)) {
p = grid.data(x, y);
} else {
p = CGAL_Nef_polyhedron2::Point(x, y);
grid.data(x, y) = p;
}
pdata_point_lists.back().push_back(p);
}
pdata.push_back(std::make_pair(pdata_point_lists.back().begin(),
pdata_point_lists.back().end()));
}
CGAL_Nef_polyhedron2 N(pdata.begin(), pdata.end(), CGAL_Nef_polyhedron2::POLYGONS);
return CGAL_Nef_polyhedron(N);
#endif
#if 0
// This version of the code works fine but is pretty slow.
//
CGAL_Nef_polyhedron2 N;
Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
for (int i = 0; i < this->polygons.size(); i++) {
std::list<CGAL_Nef_polyhedron2::Point> plist;
for (int j = 0; j < this->polygons[i].size(); j++) {
double x = this->polygons[i][j].x;
double y = this->polygons[i][j].y;
CGAL_Nef_polyhedron2::Point p;
if (grid.has(x, y)) {
p = grid.data(x, y);
} else {
p = CGAL_Nef_polyhedron2::Point(x, y);
grid.data(x, y) = p;
}
plist.push_back(p);
}
N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
}
return CGAL_Nef_polyhedron(N);
#endif
#if 1
// This version of the code does essentially the same thing as the 2nd
// version but merges some triangles before sending them to CGAL. This adds
// complexity but speeds up things..
//
struct PolyReducer
{
Grid2d<int> grid;
QHash< QPair<int,int>, QPair<int,int> > egde_to_poly;
QHash< int, CGAL_Nef_polyhedron2::Point > points;
QHash< int, QList<int> > polygons;
int poly_n;
void add_edges(int pn)
{
for (int j = 1; j <= this->polygons[pn].size(); j++) {
int a = this->polygons[pn][j-1];
int b = this->polygons[pn][j % this->polygons[pn].size()];
if (a > b) { a = a^b; b = a^b; a = a^b; }
if (this->egde_to_poly[QPair<int,int>(a, b)].first == 0)
this->egde_to_poly[QPair<int,int>(a, b)].first = pn;
else if (this->egde_to_poly[QPair<int,int>(a, b)].second == 0)
this->egde_to_poly[QPair<int,int>(a, b)].second = pn;
else
abort();
}
}
void del_poly(int pn)
{
for (int j = 1; j <= this->polygons[pn].size(); j++) {
int a = this->polygons[pn][j-1];
int b = this->polygons[pn][j % this->polygons[pn].size()];
if (a > b) { a = a^b; b = a^b; a = a^b; }
if (this->egde_to_poly[QPair<int,int>(a, b)].first == pn)
this->egde_to_poly[QPair<int,int>(a, b)].first = 0;
if (this->egde_to_poly[QPair<int,int>(a, b)].second == pn)
this->egde_to_poly[QPair<int,int>(a, b)].second = 0;
}
this->polygons.remove(pn);
}
PolyReducer(const PolySet *ps) : grid(GRID_COARSE), poly_n(1)
{
int point_n = 1;
for (int i = 0; i < ps->polygons.size(); i++) {
for (int j = 0; j < ps->polygons[i].size(); j++) {
double x = ps->polygons[i][j].x;
double y = ps->polygons[i][j].y;
if (this->grid.has(x, y)) {
int idx = this->grid.data(x, y);
// Filter away two vertices with the same index (due to grid)
// This could be done in a more general way, but we'd rather redo the entire
// grid concept instead.
if (this->polygons[this->poly_n].indexOf(idx) == -1) {
this->polygons[this->poly_n].append(this->grid.data(x, y));
}
} else {
this->grid.align(x, y) = point_n;
this->polygons[this->poly_n].append(point_n);
this->points[point_n] = CGAL_Nef_polyhedron2::Point(x, y);
point_n++;
}
}
if (this->polygons[this->poly_n].size() >= 3) {
add_edges(this->poly_n);
this->poly_n++;
}
else {
this->polygons.remove(this->poly_n);
}
}
}
int merge(int p1, int p1e, int p2, int p2e)
{
for (int i = 1; i < this->polygons[p1].size(); i++) {
int j = (p1e + i) % this->polygons[p1].size();
this->polygons[this->poly_n].append(this->polygons[p1][j]);
}
for (int i = 1; i < this->polygons[p2].size(); i++) {
int j = (p2e + i) % this->polygons[p2].size();
this->polygons[this->poly_n].append(this->polygons[p2][j]);
}
del_poly(p1);
del_poly(p2);
add_edges(this->poly_n);
return this->poly_n++;
}
void reduce()
{
QList<int> work_queue;
QHashIterator< int, QList<int> > it(polygons);
while (it.hasNext()) {
it.next();
work_queue.append(it.key());
}
while (!work_queue.isEmpty()) {
int poly1_n = work_queue.first();
work_queue.removeFirst();
if (!this->polygons.contains(poly1_n))
continue;
for (int j = 1; j <= this->polygons[poly1_n].size(); j++) {
int a = this->polygons[poly1_n][j-1];
int b = this->polygons[poly1_n][j % this->polygons[poly1_n].size()];
if (a > b) { a = a^b; b = a^b; a = a^b; }
if (this->egde_to_poly[QPair<int,int>(a, b)].first != 0 &&
this->egde_to_poly[QPair<int,int>(a, b)].second != 0) {
int poly2_n = this->egde_to_poly[QPair<int,int>(a, b)].first +
this->egde_to_poly[QPair<int,int>(a, b)].second - poly1_n;
int poly2_edge = -1;
for (int k = 1; k <= this->polygons[poly2_n].size(); k++) {
int c = this->polygons[poly2_n][k-1];
int d = this->polygons[poly2_n][k % this->polygons[poly2_n].size()];
if (c > d) { c = c^d; d = c^d; c = c^d; }
if (a == c && b == d) {
poly2_edge = k-1;
continue;
}
int poly3_n = this->egde_to_poly[QPair<int,int>(c, d)].first +
this->egde_to_poly[QPair<int,int>(c, d)].second - poly2_n;
if (poly3_n < 0)
continue;
if (poly3_n == poly1_n)
goto next_poly1_edge;
}
work_queue.append(merge(poly1_n, j-1, poly2_n, poly2_edge));
goto next_poly1;
}
next_poly1_edge:;
}
next_poly1:;
}
}
CGAL_Nef_polyhedron2 toNef()
{
CGAL_Nef_polyhedron2 N;
QHashIterator< int, QList<int> > it(polygons);
while (it.hasNext()) {
it.next();
std::list<CGAL_Nef_polyhedron2::Point> plist;
for (int j = 0; j < it.value().size(); j++) {
int p = it.value()[j];
plist.push_back(points[p]);
}
N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
}
return N;
}
};
PolyReducer pr(this);
// printf("Number of polygons before reduction: %d\n", pr.polygons.size());
pr.reduce();
// printf("Number of polygons after reduction: %d\n", pr.polygons.size());
return CGAL_Nef_polyhedron(pr.toNef());
#endif
#if 0
// This is another experimental version. I should run faster than the above,
// is a lot simpler and has only one known weakness: Degenerate polygons, which
// get repaired by GLUTess, might trigger a CGAL crash here. The only
// known case for this is triangle-with-duplicate-vertex.dxf
// FIXME: If we just did a projection, we need to recreate the border!
if (this->polygons.size() > 0) assert(this->borders.size() > 0);
CGAL_Nef_polyhedron2 N;
Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
for (int i = 0; i < this->borders.size(); i++) {
std::list<CGAL_Nef_polyhedron2::Point> plist;
for (int j = 0; j < this->borders[i].size(); j++) {
double x = this->borders[i][j].x;
double y = this->borders[i][j].y;
CGAL_Nef_polyhedron2::Point p;
if (grid.has(x, y)) {
p = grid.data(x, y);
} else {
p = CGAL_Nef_polyhedron2::Point(x, y);
grid.data(x, y) = p;
}
plist.push_back(p);
}
// FIXME: If a border (path) has a duplicate vertex in dxf,
// the CGAL_Nef_polyhedron2 constructor will crash.
N ^= CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
}
return CGAL_Nef_polyhedron(N);
#endif
}
else // not (this->is2d)
{
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
CGAL_Polyhedron P;
CGAL_Build_PolySet builder(this);
P.delegate(builder);
#if 0
std::cout << P;
#endif
CGAL_Nef_polyhedron3 N(P);
return CGAL_Nef_polyhedron(N);
}
catch (CGAL::Assertion_exception e) {
PRINTF("CGAL error: %s", e.what());
CGAL::set_error_behaviour(old_behaviour);
return CGAL_Nef_polyhedron();
}
CGAL::set_error_behaviour(old_behaviour);
}
return CGAL_Nef_polyhedron();
=======
return bbox;
>>>>>>> upstream/visitor
}

View File

@ -61,14 +61,7 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti
TransformNode *node = new TransformNode(inst);
for (int i = 0; i < 16; i++)
<<<<<<< HEAD
node->m[i] = i % 5 == 0 ? 1.0 : 0.0;
for (int i = 16; i < 19; i++)
node->m[i] = -1;
node->m[19] = 1;
=======
node->matrix[i] = i % 5 == 0 ? 1.0 : 0.0;
>>>>>>> upstream/visitor
std::vector<std::string> argnames;
std::vector<Expression*> argexpr;
@ -220,37 +213,6 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti
}
}
}
<<<<<<< HEAD
else if (this->type == COLOR)
{
Value v = c.lookup_variable("c");
if (v.type == Value::VECTOR) {
for (int i = 0; i < 4; i++)
node->matrix[16+i] = i < v.vec.size() ? v.vec[i]->num : 1.0;
// FIXME: Port to non-Qt
#if 0
} else if (v.type == Value::STRING) {
QString colorname = v.text;
QColor color;
color.setNamedColor(colorname);
if (color.isValid()) {
node->matrix[16+0] = color.redF();
node->matrix[16+1] = color.greenF();
node->matrix[16+2] = color.blueF();
} else {
PRINTF_NOCACHE("WARNING: Color name \"%s\" unknown. Please see",v.text.toUtf8().data());
PRINTF_NOCACHE("WARNING: http://en.wikipedia.org/wiki/Web_colors");
}
#endif
}
// FIXME: Only lookup alpha if color was set
Value alpha = c.lookup_variable("alpha");
if (alpha.type == Value::NUMBER) {
node->m[16+3] = alpha.num;
}
}
=======
>>>>>>> upstream/visitor
std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren();
node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
@ -280,31 +242,7 @@ std::string TransformNode::toString() const
std::string TransformNode::name() const
{
<<<<<<< HEAD
<<<<<<< HEAD
return "transform";
=======
if (dump_cache.isEmpty()) {
QString text;
if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0)
text.sprintf("n%d: color([%g, %g, %g, %g])", idx,
m[16], m[17], m[18], m[19]);
else
text.sprintf("n%d: multmatrix([[%g, %g, %g, %g], [%g, %g, %g, %g], "
"[%g, %g, %g, %g], [%g, %g, %g, %g]])", idx,
m[0], m[4], m[ 8], m[12],
m[1], m[5], m[ 9], m[13],
m[2], m[6], m[10], m[14],
m[3], m[7], m[11], m[15]);
text = indent + text + " {\n";
foreach (AbstractNode *v, children)
text += v->dump(indent + QString("\t"));
((AbstractNode*)this)->dump_cache = text + indent + "}\n";
}
return dump_cache;
=======
return "transform";
>>>>>>> upstream/visitor
}
void register_builtin_transform()

View File

@ -63,7 +63,7 @@ if (NOT $ENV{MACOSX_DEPLOY_DIR} STREQUAL "")
else()
find_package(SDL REQUIRED)
if (NOT SDL_FOUND)
message(FATAL_ERROR "SDL not found. needed for OpenCSG testing on this platform")
message(FATAL_ERROR "SDL not found.")
else()
message(STATUS "SDL library found in " ${SDL_LIBRARY})
message(STATUS "SDL header found in " ${SDL_INCLUDE_DIR})
@ -129,9 +129,25 @@ set(COMMON_SOURCES
../src/PolySetEvaluator.cc
../src/PolySetCache.cc
../src/Tree.cc
lodepng.cpp
${FLEX_OpenSCADlexer_OUTPUTS}
${BISON_OpenSCADparser_OUTPUTS})
#
# Offscreen OpenGL context source code
#
if (NOT $ENV{MACOSX_DEPLOY_DIR} STREQUAL "")
set(OFFSCREEN_CTX_SOURCE "OffscreenContext.mm")
else()
set(OFFSCREEN_CTX_SOURCE "OffscreenContext.cc")
endif()
#
# Yangli Hector Yee's comparison aglorithm
#
add_executable(yee_compare yee_compare.cpp lodepng.cpp)
#
# dumptest
#
@ -170,7 +186,7 @@ target_link_libraries(cgaltest ${CGAL_LIBRARY} ${CGAL_3RD_PARTY_LIBRARIES} ${QT_
#
# cgalpngtest
#
add_executable(cgalpngtest cgalpngtest.cc OffscreenView.cc OffscreenContext.mm
add_executable(cgalpngtest cgalpngtest.cc OffscreenView.cc ${OFFSCREEN_CTX_SOURCE}
../src/CGALRenderer.cc ../src/CGAL_Nef_polyhedron.cc ../src/cgalutils.cc
../src/CSGTermEvaluator.cc ../src/CGALEvaluator.cc ../src/CGALCache.cc
../src/PolySetCGALEvaluator.cc ../src/qhash.cc
@ -182,24 +198,12 @@ target_link_libraries(cgalpngtest ${CGAL_LIBRARY} ${CGAL_3RD_PARTY_LIBRARIES} ${
#
# opencsgtest
#
<<<<<<< HEAD
if (NOT $ENV{MACOSX_DEPLOY_DIR} STREQUAL "")
set(OFFSCREEN_SOURCE "OffscreenContext.mm")
else()
set(OFFSCREEN_SOURCE "OffscreenContext.cc")
endif()
add_executable(opencsgtest opencsgtest.cc OffscreenView.cc ${OFFSCREEN_SOURCE}
../src/cgal.cc ../src/OpenCSGRenderer.cc ../src/ThrownTogetherRenderer.cc ../src/CSGTermEvaluator.cc ../src/CGALEvaluator.cc
../src/PolySetCGALEvaluator.cc ../src/qhash.cc ../src/nef2dxf.cc
../src/cgaladv_minkowski2.cc ../src/cgaladv_minkowski3.cc
=======
add_executable(opencsgtest opencsgtest.cc OffscreenView.cc OffscreenContext.mm
add_executable(opencsgtest opencsgtest.cc OffscreenView.cc ${OFFSCREEN_CTX_SOURCE}
../src/OpenCSGRenderer.cc ../src/ThrownTogetherRenderer.cc
../src/CSGTermEvaluator.cc ../src/CGAL_Nef_polyhedron.cc ../src/cgalutils.cc
../src/CGALEvaluator.cc ../src/CGALCache.cc ../src/PolySetCGALEvaluator.cc ../src/qhash.cc
../src/CGAL_Nef_polyhedron_DxfData.cc ../src/cgaladv_minkowski2.cc ../src/cgaladv_convexhull2.cc
>>>>>>> upstream/visitor
${COMMON_SOURCES})
set_target_properties(opencsgtest PROPERTIES COMPILE_FLAGS "-DENABLE_OPENCSG -DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}")
target_link_libraries(opencsgtest ${CGAL_LIBRARY} ${CGAL_3RD_PARTY_LIBRARIES} ${QT_LIBRARIES} ${OPENCSG_LIBRARY} ${GLEW_LIBRARY} ${COCOA_LIBRARY} ${OPENGL_LIBRARY})
@ -232,15 +236,6 @@ add_cmdline_test(csgtexttest txt ${MINIMAL_FILES})
add_cmdline_test(csgtermtest txt ${MINIMAL_FILES})
# Add cgaltest tests to CTest
<<<<<<< HEAD
LIST(APPEND CGALTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/minimal/cube.scad)
LIST(APPEND CGALTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/minimal/sphere.scad)
LIST(APPEND CGALTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/minimal/cylinder.scad)
LIST(APPEND CGALTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/features/background-modifier.scad)
LIST(APPEND CGALTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/features/highlight-modifier.scad)
LIST(APPEND CGALTEST_FILES ${CMAKE_SOURCE_DIR}/../examples/example001.scad)
=======
>>>>>>> upstream/visitor
add_cmdline_test(cgaltest stl ${CGALTEST_FILES})
# Add cgalpngtest tests to CTest

View File

@ -23,26 +23,14 @@ IF (WIN32)
${PROJECT_SOURCE_DIR}/src/nvgl/glew/lib
DOC "The GLEW library")
ELSE (WIN32)
<<<<<<< HEAD
MESSAGE( "-- GLEW_DIR value:" ${GLEW_DIR})
FIND_PATH( GLEW_INCLUDE_PATH GL/glew.h
PATHS /usr/include /usr/local/include
${GLEW_DIR}/include
=======
message("GLEW_DIR: " ${GLEW_DIR})
FIND_PATH( GLEW_INCLUDE_PATH GL/glew.h
PATHS ${GLEW_DIR}/include /usr/include /usr/local/include
>>>>>>> upstream/visitor
NO_DEFAULT_PATH
DOC "The directory where GL/glew.h resides")
FIND_LIBRARY( GLEW_LIBRARY
NAMES GLEW glew
<<<<<<< HEAD
PATHS /usr/lib /usr/local/lib
${GLEW_DIR}/lib
=======
PATHS ${GLEW_DIR}/lib /usr/lib /usr/local/lib
>>>>>>> upstream/visitor
NO_DEFAULT_PATH
DOC "The GLEW library")
ENDIF (WIN32)

View File

@ -1,4 +1,6 @@
#include "OffscreenContext.h"
#include "printutils.h"
#include "lodepng.h"
// see http://www.gamedev.net/topic/552607-conflict-between-glew-and-sdl/
#define NO_SDL_GLEXT
@ -25,6 +27,52 @@ struct OffscreenContext
GLuint depthbo;
};
void write_targa(const char *filename, GLubyte *pixels, int width, int height)
{
FILE *f = fopen( filename, "w" );
int y;
if (f) {
GLubyte header[] = {
00,00,02, 00,00,00, 00,00,00, 00,00,00,
0xff & width, 0xff & width >> 8,
0xff & height, 0xff & height >> 8,
32, 0x20 }; // next-to-last = bit depth
fwrite( header, sizeof(header), 1, f);
for (y=height-1; y>=0; y--)
fwrite( pixels + y*width*4, 4, width, f);
fclose(f);
}
}
void write_png(const char *filename, GLubyte *pixels, int width, int height)
{
size_t pixel_size = 4;
size_t dataout_size = -1;
GLubyte *dataout = (GLubyte*)malloc(width*height*pixel_size); // freed below
GLubyte *pixels_flipped = (GLubyte*)malloc(width*height*pixel_size); // freed below
for (int y=0;y<height;y++) {
for (int x=0;x<width;x++) {
int offs1 = y*width*pixel_size + x*pixel_size;
int offs2 = (height-1-y)*width*pixel_size + x*pixel_size;
pixels_flipped[offs1 ] = pixels[offs2 ];
pixels_flipped[offs1+1] = pixels[offs2+1];
pixels_flipped[offs1+2] = pixels[offs2+2];
pixels_flipped[offs1+3] = pixels[offs2+3];
}
}
//encoder.settings.zlibsettings.windowSize = 2048;
//LodePNG_Text_add(&encoder.infoPng.text, "Comment", "Created with LodePNG");
LodePNG_encode(&dataout, &dataout_size, pixels_flipped, width, height, LCT_RGBA, 8);
//LodePNG_saveFile(dataout, dataout_size, "blah2.png");
FILE *f = fopen( filename, "w" );
if (f) {
fwrite( dataout, 1, dataout_size, f);
fclose(f);
}
free(pixels_flipped);
free(dataout);
}
OffscreenContext *create_offscreen_context(int w, int h)
{
@ -34,11 +82,11 @@ OffscreenContext *create_offscreen_context(int w, int h)
// dummy window
SDL_Init(SDL_INIT_VIDEO);
SDL_SetVideoMode(256,256,32,SDL_OPENGL);
SDL_SetVideoMode(ctx->width,ctx->height,32,SDL_OPENGL);
// must come after openGL context init (done by dummy window)
// but must also come before various EXT calls
glewInit();
//glewInit();
/*
// Test if framebuffer objects are supported
@ -84,8 +132,27 @@ OffscreenContext *create_offscreen_context(int w, int h)
if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) !=
GL_FRAMEBUFFER_COMPLETE_EXT)
REPORT_ERROR_AND_EXIT("Problem with OpenGL framebuffer after specifying depth render buffer.");
*/
/*
glClearColor(1, 1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glColor3f( 1, 0, 0);
glVertex3f( 0, 0, 0);
glVertex3f( 1, 0, 0);
glVertex3f( 0, 1, 0);
glEnd();
SDL_GL_SwapBuffers();
// sleep(2);
*/
int samplesPerPixel = 4; // R, G, B and A
/* char * filename = "blah.tga";
GLubyte pixels[ ctx->width * ctx->height * samplesPerPixel ];
glReadPixels(0, 0, ctx->width, ctx->height, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
printf("writing %s\n",filename);
write_targa(filename,pixels,ctx->width, ctx->height);*/
return ctx;
}
@ -100,35 +167,25 @@ bool teardown_offscreen_context(OffscreenContext *ctx)
return true;
}
void write_targa(const char *filename, GLubyte *pixels, int width, int height)
{
FILE *f = fopen( filename, "w" );
int y;
if (f) {
GLubyte header[] = {
00,00,02, 00,00,00, 00,00,00, 00,00,00,
0xff & width, 0xff & width >> 8,
0xff & height, 0xff & height >> 8,
32, 0x20 }; // next-to-last = bit depth
fwrite( header, sizeof(header), 1, f);
for (y=height-1; y>=0; y--)
fwrite( pixels + y*width*4, 4, width, f);
fclose(f);
}
}
bool save_framebuffer(OffscreenContext *ctx, const char *filename)
{
/*
* Extract the resulting rendering as an image
*/
int samplesPerPixel = 4; // R, G, B and A
SDL_GL_SwapBuffers(); // show image
int samplesPerPixel = 4; // R, G, B and A
GLubyte pixels[ ctx->width * ctx->height * samplesPerPixel ];
glReadPixels(0, 0, ctx->width, ctx->height, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
printf("writing %s\n",filename);
write_targa(filename,pixels,ctx->width, ctx->height);
glReadPixels(0, 0, ctx->width, ctx->height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
//char * filename2="blah2.tga";
//PRINTF("writing %s\n",filename2);
//write_targa(filename2,pixels,ctx->width, ctx->height);
char * filename2="blah2.png";
PRINTF("writing %s . . .",filename);
//write_targa(filename2,pixels,ctx->width, ctx->height);
write_png(filename,pixels,ctx->width, ctx->height);
PRINTF("written\n");
return true;
}

View File

@ -180,7 +180,7 @@ int main(int argc, char **argv)
csgInfo.glview = new OffscreenView(512,512);
glewInit();
//glewInit();
#ifdef DEBUG
cout << "GLEW version " << glewGetString(GLEW_VERSION) << "\n";
cout << (const char *)glGetString(GL_RENDERER) << "(" << (const char *)glGetString(GL_VENDOR) << ")\n"

View File

@ -144,17 +144,11 @@ int main(int argc, char **argv)
// cout << tree.getString(*root_node) << "\n";
<<<<<<< HEAD
CSGTermEvaluator evaluator(tree);
vector<CSGTerm*> empty = vector<CSGTerm*>();
CSGTerm *root_term = evaluator.evaluateCSGTerm(*root_node, empty, empty);
=======
vector<CSGTerm*> highlights;
vector<CSGTerm*> background;
PolySetEvaluator psevaluator(tree);
CSGTermEvaluator evaluator(tree, &psevaluator);
CSGTerm *root_term = evaluator.evaluateCSGTerm(*root_node, highlights, background);
>>>>>>> upstream/visitor
// cout << "Stored terms: " << evaluator.stored_term.size() << "\n";
// for (map<int, class CSGTerm*>::iterator iter = evaluator.stored_term.begin();

5797
tests/lodepng.cpp Normal file

File diff suppressed because it is too large Load Diff

1893
tests/lodepng.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -43,8 +43,15 @@ def verify_test(testname, cmd):
return True
def execute_and_redirect(cmd, params, outfile):
proc = subprocess.Popen([cmd] + params, stdout=outfile)
retval = proc.wait()
retval = -1
try:
proc = subprocess.Popen([cmd] + params, stdout=outfile)
retval = proc.wait()
except OSError as (errno, strerror):
print >> sys.stderr, "Error: ", errno, strerror
print >> sys.stderr, " cmd:", cmd
print >> sys.stderr, " params:", params
print >> sys.stderr, " outfile:", outfile
return retval
def get_normalized_text(filename):
@ -61,7 +68,7 @@ def compare_default(resultfilename):
return True
def compare_png(resultfilename):
if execute_and_redirect("diff", [expectedfilename, resultfilename], sys.stderr) != 0:
if execute_and_redirect("./yee_compare", [expectedfilename, resultfilename], sys.stderr) != 0:
return False
return True

680
tests/yee_compare.cpp Normal file
View File

@ -0,0 +1,680 @@
#include "yee_compare.h"
#include "lodepng.h"
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <math.h>
static const char* copyright =
"PerceptualDiff version 1.1.1, Copyright (C) 2006 Yangli Hector Yee\n\
PerceptualDiff comes with ABSOLUTELY NO WARRANTY;\n\
This is free software, and you are welcome\n\
to redistribute it under certain conditions;\n\
See the GPL page for details: http://www.gnu.org/copyleft/gpl.html\n\n";
static const char *usage =
"PeceptualDiff image1.tif image2.tif\n\n\
Compares image1.tif and image2.tif using a perceptually based image metric\n\
Options:\n\
\t-verbose : Turns on verbose mode\n\
\t-fov deg : Field of view in degrees (0.1 to 89.9)\n\
\t-threshold p : #pixels p below which differences are ignored\n\
\t-gamma g : Value to convert rgb into linear space (default 2.2)\n\
\t-luminance l : White luminance (default 100.0 cdm^-2)\n\
\t-luminanceonly : Only consider luminance; ignore chroma (color) in the comparison\n\
\t-colorfactor : How much of color to use, 0.0 to 1.0, 0.0 = ignore color.\n\
\t-downsample : How many powers of two to down sample the image.\n\
\t-output o.ppm : Write difference to the file o.ppm\n\
\n\
\n Note: Input or Output files can also be in the PNG or JPG format or any format\
\n that FreeImage supports.\
\n";
CompareArgs::CompareArgs()
{
ImgA = NULL;
ImgB = NULL;
ImgDiff = NULL;
Verbose = false;
LuminanceOnly = false;
FieldOfView = 45.0f;
Gamma = 2.2f;
ThresholdPixels = 100;
Luminance = 100.0f;
ColorFactor = 1.0f;
DownSample = 0;
}
CompareArgs::~CompareArgs()
{
if (ImgA) delete ImgA;
if (ImgB) delete ImgB;
if (ImgDiff) delete ImgDiff;
}
bool CompareArgs::Parse_Args(int argc, char **argv)
{
if (argc < 3) {
ErrorStr = copyright;
ErrorStr += usage;
return false;
}
int image_count = 0;
const char* output_file_name = NULL;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-fov") == 0) {
if (++i < argc) {
FieldOfView = (float) atof(argv[i]);
}
} else if (strcmp(argv[i], "-verbose") == 0) {
Verbose = true;
} else if (strcmp(argv[i], "-threshold") == 0) {
if (++i < argc) {
ThresholdPixels = atoi(argv[i]);
}
} else if (strcmp(argv[i], "-gamma") == 0) {
if (++i < argc) {
Gamma = (float) atof(argv[i]);
}
} else if (strcmp(argv[i], "-luminance") == 0) {
if (++i < argc) {
Luminance = (float) atof(argv[i]);
}
} else if (strcmp(argv[i], "-luminanceonly") == 0) {
LuminanceOnly = true;
} else if (strcmp(argv[i], "-colorfactor") == 0) {
if (++i < argc) {
ColorFactor = (float) atof(argv[i]);
}
} else if (strcmp(argv[i], "-downsample") == 0) {
if (++i < argc) {
DownSample = (int) atoi(argv[i]);
}
} else if (strcmp(argv[i], "-output") == 0) {
if (++i < argc) {
output_file_name = argv[i];
}
} else if (image_count < 2) {
RGBAImage* img = RGBAImage::ReadFromFile(argv[i]);
if (!img) {
ErrorStr = "FAIL: Cannot open ";
ErrorStr += argv[i];
ErrorStr += "\n";
return false;
} else {
++image_count;
if(image_count == 1)
ImgA = img;
else
ImgB = img;
}
} else {
fprintf(stderr, "Warning: option/file \"%s\" ignored\n", argv[i]);
}
} // i
if(!ImgA || !ImgB) {
ErrorStr = "FAIL: Not enough image files specified\n";
return false;
}
for (int i = 0; i < DownSample; i++) {
if (Verbose) printf("Downsampling by %d\n", 1 << (i+1));
RGBAImage *tmp = ImgA->DownSample();
if (tmp) {
delete ImgA;
ImgA = tmp;
}
tmp = ImgB->DownSample();
if (tmp) {
delete ImgB;
ImgB = tmp;
}
}
if(output_file_name) {
ImgDiff = new RGBAImage(ImgA->Get_Width(), ImgA->Get_Height(), output_file_name);
}
return true;
}
void CompareArgs::Print_Args()
{
printf("Field of view is %f degrees\n", FieldOfView);
printf("Threshold pixels is %d pixels\n", ThresholdPixels);
printf("The Gamma is %f\n", Gamma);
printf("The Display's luminance is %f candela per meter squared\n", Luminance);
}
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
LPyramid::LPyramid(float *image, int width, int height) :
Width(width),
Height(height)
{
// Make the Laplacian pyramid by successively
// copying the earlier levels and blurring them
for (int i=0; i<MAX_PYR_LEVELS; i++) {
if (i == 0) {
Levels[i] = Copy(image);
} else {
Levels[i] = new float[Width * Height];
Convolve(Levels[i], Levels[i - 1]);
}
}
}
LPyramid::~LPyramid()
{
for (int i=0; i<MAX_PYR_LEVELS; i++) {
if (Levels[i]) delete Levels[i];
}
}
float *LPyramid::Copy(float *img)
{
int max = Width * Height;
float *out = new float[max];
for (int i = 0; i < max; i++) out[i] = img[i];
return out;
}
void LPyramid::Convolve(float *a, float *b)
// convolves image b with the filter kernel and stores it in a
{
int y,x,i,j,nx,ny;
const float Kernel[] = {0.05f, 0.25f, 0.4f, 0.25f, 0.05f};
for (y=0; y<Height; y++) {
for (x=0; x<Width; x++) {
int index = y * Width + x;
a[index] = 0.0f;
for (i=-2; i<=2; i++) {
for (j=-2; j<=2; j++) {
nx=x+i;
ny=y+j;
if (nx<0) nx=-nx;
if (ny<0) ny=-ny;
if (nx>=Width) nx=2*Width-nx-1;
if (ny>=Height) ny=2*Height-ny-1;
a[index] += Kernel[i+2] * Kernel[j+2] * b[ny * Width + nx];
}
}
}
}
}
float LPyramid::Get_Value(int x, int y, int level)
{
int index = x + y * Width;
int l = level;
if (l > MAX_PYR_LEVELS) l = MAX_PYR_LEVELS;
return Levels[level][index];
}
#ifndef M_PI
#define M_PI 3.14159265f
#endif
/*
* Given the adaptation luminance, this function returns the
* threshold of visibility in cd per m^2
* TVI means Threshold vs Intensity function
* This version comes from Ward Larson Siggraph 1997
*/
float tvi(float adaptation_luminance)
{
// returns the threshold luminance given the adaptation luminance
// units are candelas per meter squared
float log_a, r, result;
log_a = log10f(adaptation_luminance);
if (log_a < -3.94f) {
r = -2.86f;
} else if (log_a < -1.44f) {
r = powf(0.405f * log_a + 1.6f , 2.18f) - 2.86f;
} else if (log_a < -0.0184f) {
r = log_a - 0.395f;
} else if (log_a < 1.9f) {
r = powf(0.249f * log_a + 0.65f, 2.7f) - 0.72f;
} else {
r = log_a - 1.255f;
}
result = powf(10.0f , r);
return result;
}
// computes the contrast sensitivity function (Barten SPIE 1989)
// given the cycles per degree (cpd) and luminance (lum)
float csf(float cpd, float lum)
{
float a, b, result;
a = 440.0f * powf((1.0f + 0.7f / lum), -0.2f);
b = 0.3f * powf((1.0f + 100.0f / lum), 0.15f);
result = a * cpd * expf(-b * cpd) * sqrtf(1.0f + 0.06f * expf(b * cpd));
return result;
}
/*
* Visual Masking Function
* from Daly 1993
*/
float mask(float contrast)
{
float a, b, result;
a = powf(392.498f * contrast, 0.7f);
b = powf(0.0153f * a, 4.0f);
result = powf(1.0f + b, 0.25f);
return result;
}
// convert Adobe RGB (1998) with reference white D65 to XYZ
void AdobeRGBToXYZ(float r, float g, float b, float &x, float &y, float &z)
{
// matrix is from http://www.brucelindbloom.com/
x = r * 0.576700f + g * 0.185556f + b * 0.188212f;
y = r * 0.297361f + g * 0.627355f + b * 0.0752847f;
z = r * 0.0270328f + g * 0.0706879f + b * 0.991248f;
}
void XYZToLAB(float x, float y, float z, float &L, float &A, float &B)
{
static float xw = -1;
static float yw;
static float zw;
// reference white
if (xw < 0) {
AdobeRGBToXYZ(1, 1, 1, xw, yw, zw);
}
const float epsilon = 216.0f / 24389.0f;
const float kappa = 24389.0f / 27.0f;
float f[3];
float r[3];
r[0] = x / xw;
r[1] = y / yw;
r[2] = z / zw;
for (int i = 0; i < 3; i++) {
if (r[i] > epsilon) {
f[i] = powf(r[i], 1.0f / 3.0f);
} else {
f[i] = (kappa * r[i] + 16.0f) / 116.0f;
}
}
L = 116.0f * f[1] - 16.0f;
A = 500.0f * (f[0] - f[1]);
B = 200.0f * (f[1] - f[2]);
}
bool Yee_Compare(CompareArgs &args)
{
if ((args.ImgA->Get_Width() != args.ImgB->Get_Width()) ||
(args.ImgA->Get_Height() != args.ImgB->Get_Height())) {
args.ErrorStr = "Image dimensions do not match\n";
return false;
}
unsigned int i, dim;
dim = args.ImgA->Get_Width() * args.ImgA->Get_Height();
bool identical = true;
for (i = 0; i < dim; i++) {
if (args.ImgA->Get(i) != args.ImgB->Get(i)) {
identical = false;
break;
}
}
if (identical) {
args.ErrorStr = "Images are binary identical\n";
return true;
}
// assuming colorspaces are in Adobe RGB (1998) convert to XYZ
float *aX = new float[dim];
float *aY = new float[dim];
float *aZ = new float[dim];
float *bX = new float[dim];
float *bY = new float[dim];
float *bZ = new float[dim];
float *aLum = new float[dim];
float *bLum = new float[dim];
float *aA = new float[dim];
float *bA = new float[dim];
float *aB = new float[dim];
float *bB = new float[dim];
if (args.Verbose) printf("Converting RGB to XYZ\n");
unsigned int x, y, w, h;
w = args.ImgA->Get_Width();
h = args.ImgA->Get_Height();
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
float r, g, b, l;
i = x + y * w;
r = powf(args.ImgA->Get_Red(i) / 255.0f, args.Gamma);
g = powf(args.ImgA->Get_Green(i) / 255.0f, args.Gamma);
b = powf(args.ImgA->Get_Blue(i) / 255.0f, args.Gamma);
AdobeRGBToXYZ(r,g,b,aX[i],aY[i],aZ[i]);
XYZToLAB(aX[i], aY[i], aZ[i], l, aA[i], aB[i]);
r = powf(args.ImgB->Get_Red(i) / 255.0f, args.Gamma);
g = powf(args.ImgB->Get_Green(i) / 255.0f, args.Gamma);
b = powf(args.ImgB->Get_Blue(i) / 255.0f, args.Gamma);
AdobeRGBToXYZ(r,g,b,bX[i],bY[i],bZ[i]);
XYZToLAB(bX[i], bY[i], bZ[i], l, bA[i], bB[i]);
aLum[i] = aY[i] * args.Luminance;
bLum[i] = bY[i] * args.Luminance;
}
}
if (args.Verbose) printf("Constructing Laplacian Pyramids\n");
LPyramid *la = new LPyramid(aLum, w, h);
LPyramid *lb = new LPyramid(bLum, w, h);
float num_one_degree_pixels = (float) (2 * tan( args.FieldOfView * 0.5 * M_PI / 180) * 180 / M_PI);
float pixels_per_degree = w / num_one_degree_pixels;
if (args.Verbose) printf("Performing test\n");
float num_pixels = 1;
unsigned int adaptation_level = 0;
for (i = 0; i < MAX_PYR_LEVELS; i++) {
adaptation_level = i;
if (num_pixels > num_one_degree_pixels) break;
num_pixels *= 2;
}
float cpd[MAX_PYR_LEVELS];
cpd[0] = 0.5f * pixels_per_degree;
for (i = 1; i < MAX_PYR_LEVELS; i++) cpd[i] = 0.5f * cpd[i - 1];
float csf_max = csf(3.248f, 100.0f);
float F_freq[MAX_PYR_LEVELS - 2];
for (i = 0; i < MAX_PYR_LEVELS - 2; i++) F_freq[i] = csf_max / csf( cpd[i], 100.0f);
unsigned int pixels_failed = 0;
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
int index = x + y * w;
float contrast[MAX_PYR_LEVELS - 2];
float sum_contrast = 0;
for (i = 0; i < MAX_PYR_LEVELS - 2; i++) {
float n1 = fabsf(la->Get_Value(x,y,i) - la->Get_Value(x,y,i + 1));
float n2 = fabsf(lb->Get_Value(x,y,i) - lb->Get_Value(x,y,i + 1));
float numerator = (n1 > n2) ? n1 : n2;
float d1 = fabsf(la->Get_Value(x,y,i+2));
float d2 = fabsf(lb->Get_Value(x,y,i+2));
float denominator = (d1 > d2) ? d1 : d2;
if (denominator < 1e-5f) denominator = 1e-5f;
contrast[i] = numerator / denominator;
sum_contrast += contrast[i];
}
if (sum_contrast < 1e-5) sum_contrast = 1e-5f;
float F_mask[MAX_PYR_LEVELS - 2];
float adapt = la->Get_Value(x,y,adaptation_level) + lb->Get_Value(x,y,adaptation_level);
adapt *= 0.5f;
if (adapt < 1e-5) adapt = 1e-5f;
for (i = 0; i < MAX_PYR_LEVELS - 2; i++) {
F_mask[i] = mask(contrast[i] * csf(cpd[i], adapt));
}
float factor = 0;
for (i = 0; i < MAX_PYR_LEVELS - 2; i++) {
factor += contrast[i] * F_freq[i] * F_mask[i] / sum_contrast;
}
if (factor < 1) factor = 1;
if (factor > 10) factor = 10;
float delta = fabsf(la->Get_Value(x,y,0) - lb->Get_Value(x,y,0));
bool pass = true;
// pure luminance test
if (delta > factor * tvi(adapt)) {
pass = false;
} else if (!args.LuminanceOnly) {
// CIE delta E test with modifications
float color_scale = args.ColorFactor;
// ramp down the color test in scotopic regions
if (adapt < 10.0f) {
// Don't do color test at all.
color_scale = 0.0;
}
float da = aA[index] - bA[index];
float db = aB[index] - bB[index];
da = da * da;
db = db * db;
float delta_e = (da + db) * color_scale;
if (delta_e > factor) {
pass = false;
}
}
if (!pass) {
pixels_failed++;
if (args.ImgDiff) {
args.ImgDiff->Set(255, 0, 0, 255, index);
}
} else {
if (args.ImgDiff) {
args.ImgDiff->Set(0, 0, 0, 255, index);
}
}
}
}
if (aX) delete[] aX;
if (aY) delete[] aY;
if (aZ) delete[] aZ;
if (bX) delete[] bX;
if (bY) delete[] bY;
if (bZ) delete[] bZ;
if (aLum) delete[] aLum;
if (bLum) delete[] bLum;
if (la) delete la;
if (lb) delete lb;
if (aA) delete aA;
if (bA) delete bA;
if (aB) delete aB;
if (bB) delete bB;
char different[100];
sprintf(different, "%d pixels are different\n", pixels_failed);
// Always output image difference if requested.
if (args.ImgDiff) {
if (args.ImgDiff->WriteToFile(args.ImgDiff->Get_Name().c_str())) {
args.ErrorStr += "Wrote difference image to ";
args.ErrorStr+= args.ImgDiff->Get_Name();
args.ErrorStr += "\n";
} else {
args.ErrorStr += "Could not write difference image to ";
args.ErrorStr+= args.ImgDiff->Get_Name();
args.ErrorStr += "\n";
}
}
if (pixels_failed < args.ThresholdPixels) {
args.ErrorStr = "Images are perceptually indistinguishable\n";
args.ErrorStr += different;
return true;
}
args.ErrorStr = "Images are visibly different\n";
args.ErrorStr += different;
return false;
}
RGBAImage* RGBAImage::DownSample() const {
if (Width <=1 || Height <=1) return NULL;
int nw = Width / 2;
int nh = Height / 2;
RGBAImage* img = new RGBAImage(nw, nh, Name.c_str());
for (int y = 0; y < nh; y++) {
for (int x = 0; x < nw; x++) {
int d[4];
// Sample a 2x2 patch from the parent image.
d[0] = Get(2 * x + 0, 2 * y + 0);
d[1] = Get(2 * x + 1, 2 * y + 0);
d[2] = Get(2 * x + 0, 2 * y + 1);
d[3] = Get(2 * x + 1, 2 * y + 1);
int rgba = 0;
// Find the average color.
for (int i = 0; i < 4; i++) {
int c = (d[0] >> (8 * i)) & 0xFF;
c += (d[1] >> (8 * i)) & 0xFF;
c += (d[2] >> (8 * i)) & 0xFF;
c += (d[3] >> (8 * i)) & 0xFF;
c /= 4;
rgba |= (c & 0xFF) << (8 * i);
}
img->Set(x, y, rgba);
}
}
return img;
}
bool RGBAImage::WriteToFile(const char* filename)
{
LodePNG::Encoder encoder;
encoder.addText("Comment","lodepng");
encoder.getSettings().zlibsettings.windowSize = 2048;
/*
const FREE_IMAGE_FORMAT fileType = FreeImage_GetFIFFromFilename(filename);
if(FIF_UNKNOWN == fileType)
{
printf("Can't save to unknown filetype %s\n", filename);
return false;
}
FIBITMAP* bitmap = FreeImage_Allocate(Width, Height, 32, 0x000000ff, 0x0000ff00, 0x00ff0000);
if(!bitmap)
{
printf("Failed to create freeimage for %s\n", filename);
return false;
}
const unsigned int* source = Data;
for( int y=0; y < Height; y++, source += Width )
{
unsigned int* scanline = (unsigned int*)FreeImage_GetScanLine(bitmap, Height - y - 1 );
memcpy(scanline, source, sizeof(source[0]) * Width);
}
FreeImage_SetTransparent(bitmap, false);
FIBITMAP* converted = FreeImage_ConvertTo24Bits(bitmap);
const bool result = !!FreeImage_Save(fileType, converted, filename);
if(!result)
printf("Failed to save to %s\n", filename);
FreeImage_Unload(converted);
FreeImage_Unload(bitmap);
return result;
*/
return true;
}
RGBAImage* RGBAImage::ReadFromFile(const char* filename)
{
unsigned char* buffer;
unsigned char* image;
size_t buffersize, imagesize, i;
LodePNG_Decoder decoder;
LodePNG_loadFile(&buffer, &buffersize, filename); /*load the image file with given filename*/
LodePNG_Decoder_init(&decoder);
LodePNG_Decoder_decode(&decoder, &image, &imagesize, buffer, buffersize); /*decode the png*/
/*load and decode*/
/*if there's an error, display it, otherwise display information about the image*/
if(decoder.error) printf("error %u: %s\n", decoder.error, LodePNG_error_text(decoder.error));
int w = decoder.infoPng.width;
int h = decoder.infoPng.height;
RGBAImage* result = new RGBAImage(w, h, filename);
// Copy the image over to our internal format, FreeImage has the scanlines bottom to top though.
unsigned int* dest = result->Data;
memcpy(dest, (void *)image, h*w*4);
/*cleanup decoder*/
free(image);
free(buffer);
LodePNG_Decoder_cleanup(&decoder);
return result;
/*
const FREE_IMAGE_FORMAT fileType = FreeImage_GetFileType(filename);
if(FIF_UNKNOWN == fileType)
{
printf("Unknown filetype %s\n", filename);
return 0;
}
FIBITMAP* freeImage = 0;
if(FIBITMAP* temporary = FreeImage_Load(fileType, filename, 0))
{
freeImage = FreeImage_ConvertTo32Bits(temporary);
FreeImage_Unload(temporary);
}
if(!freeImage)
{
printf( "Failed to load the image %s\n", filename);
return 0;
}
const int w = FreeImage_GetWidth(freeImage);
const int h = FreeImage_GetHeight(freeImage);
RGBAImage* result = new RGBAImage(w, h, filename);
// Copy the image over to our internal format, FreeImage has the scanlines bottom to top though.
unsigned int* dest = result->Data;
for( int y=0; y < h; y++, dest += w )
{
const unsigned int* scanline = (const unsigned int*)FreeImage_GetScanLine(freeImage, h - y - 1 );
memcpy(dest, scanline, sizeof(dest[0]) * w);
}
FreeImage_Unload(freeImage);
return result;
return NULL;
*/
}
int main(int argc, char **argv)
{
CompareArgs args;
if (!args.Parse_Args(argc, argv)) {
printf("%s", args.ErrorStr.c_str());
return -1;
} else {
if (args.Verbose) args.Print_Args();
}
const bool passed = Yee_Compare(args);
if (passed) {
if(args.Verbose)
printf("PASS: %s\n", args.ErrorStr.c_str());
} else {
printf("FAIL: %s\n", args.ErrorStr.c_str());
}
return passed ? 0 : 1;
}

125
tests/yee_compare.h Normal file
View File

@ -0,0 +1,125 @@
/*
Metric
RGBAImage.h
Comapre Args
Laplacian Pyramid
Copyright (C) 2006 Yangli Hector Yee
This program is free software; you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program;
if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _yee_compare_h
#define _yee_compare_h
#include <string>
class RGBAImage;
// Args to pass into the comparison function
class CompareArgs
{
public:
CompareArgs();
~CompareArgs();
bool Parse_Args(int argc, char **argv);
void Print_Args();
RGBAImage *ImgA; // Image A
RGBAImage *ImgB; // Image B
RGBAImage *ImgDiff; // Diff image
bool Verbose; // Print lots of text or not
bool LuminanceOnly; // Only consider luminance; ignore chroma channels in the comparison.
float FieldOfView; // Field of view in degrees
float Gamma; // The gamma to convert to linear color space
float Luminance; // the display's luminance
unsigned int ThresholdPixels; // How many pixels different to ignore
std::string ErrorStr; // Error string
// How much color to use in the metric.
// 0.0 is the same as LuminanceOnly = true,
// 1.0 means full strength.
float ColorFactor;
// How much to down sample image before comparing, in powers of 2.
int DownSample;
};
#define MAX_PYR_LEVELS 8
class LPyramid
{
public:
LPyramid(float *image, int width, int height);
virtual ~LPyramid();
float Get_Value(int x, int y, int level);
protected:
float *Copy(float *img);
void Convolve(float *a, float *b);
// Succesively blurred versions of the original image
float *Levels[MAX_PYR_LEVELS];
int Width;
int Height;
};
class CompareArgs;
// Image comparison metric using Yee's method
// References: A Perceptual Metric for Production Testing, Hector Yee, Journal of Graphics Tools 2004
bool Yee_Compare(CompareArgs &args);
/** Class encapsulating an image containing R,G,B,A channels.
*
* Internal representation assumes data is in the ABGR format, with the RGB
* color channels premultiplied by the alpha value. Premultiplied alpha is
* often also called "associated alpha" - see the tiff 6 specification for some
* discussion - http://partners.adobe.com/asn/developer/PDFS/TN/TIFF6.pdf
*
*/
class RGBAImage
{
RGBAImage(const RGBAImage&);
RGBAImage& operator=(const RGBAImage&);
public:
RGBAImage(int w, int h, const char *name = 0)
{
Width = w;
Height = h;
if (name) Name = name;
Data = new unsigned int[w * h];
};
~RGBAImage() { if (Data) delete[] Data; }
unsigned char Get_Red(unsigned int i) { return (Data[i] & 0xFF); }
unsigned char Get_Green(unsigned int i) { return ((Data[i]>>8) & 0xFF); }
unsigned char Get_Blue(unsigned int i) { return ((Data[i]>>16) & 0xFF); }
unsigned char Get_Alpha(unsigned int i) { return ((Data[i]>>24) & 0xFF); }
void Set(unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned int i)
{ Data[i] = r | (g << 8) | (b << 16) | (a << 24); }
int Get_Width(void) const { return Width; }
int Get_Height(void) const { return Height; }
void Set(int x, int y, unsigned int d) { Data[x + y * Width] = d; }
unsigned int Get(int x, int y) const { return Data[x + y * Width]; }
unsigned int Get(int i) const { return Data[i]; }
const std::string &Get_Name(void) const { return Name; }
RGBAImage* DownSample() const;
bool WriteToFile(const char* filename);
static RGBAImage* ReadFromFile(const char* filename);
protected:
int Width;
int Height;
std::string Name;
unsigned int *Data;
};
#endif