diff --git a/.travis.yml b/.travis.yml index ef4c8f67f..33c3bdd8f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,8 +27,15 @@ matrix: - libglew1.5-dev - libglm-dev - libglew-dev + - libglfw3-dev - lcov - rapidjson-dev + - libgl1-mesa-dev + - libgl1-mesa-dri + - libgl1-mesa-glx + - libglapi-mesa + - libgles2-mesa + - s3cmd services: - xvfb @@ -40,7 +47,19 @@ matrix: - cmake --version - ./scripts/travis.sh + after_failure: + - | + bash -c 'echo "[default] + host_base=${S3_HOST_BASE} + host_bucket=${S3_HOST_BUCKET} + bucket_location=${S3_BUCKET_LOCATION} + use_https=True + access_key=${S3_ACCESS_KEY} + secret_key=${S3_SECRET_KEY}" > .s3cfg' + - s3cmd --recursive --config=.s3cfg put /home/travis/build/LibreCAD/LibreCAD_3/unittest/rendering/res s3://librecad-s3-artifacts + cache: ccache before_script: - "export DISPLAY=:99.0" + - "export LIBGL_ALWAYS_SOFTWARE=true" diff --git a/luacmdinterface/CMakeLists.txt b/luacmdinterface/CMakeLists.txt index b3deabd1a..5670c2e80 100644 --- a/luacmdinterface/CMakeLists.txt +++ b/luacmdinterface/CMakeLists.txt @@ -5,20 +5,13 @@ set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") message("***** Lua command line interface *****") -# Cairo -find_package(Cairo REQUIRED) -include_directories(${CAIRO_INCLUDE_DIRS}) - -# Pango -find_package(Pango 1.36 REQUIRED) -include_directories(${PANGO_INCLUDE_DIRS}) -link_directories(${PANGO_LIBRARY_DIRS}) - # Curl find_package(CURL REQUIRED) include_directories(${CURL_INCLUDE_DIRS}) link_directories(${CURL_LIBRARY_DIRS}) +find_package(glfw3 3.2 REQUIRED) + # Eigen 3 find_package(Eigen3 REQUIRED) if( CMAKE_COMPILER_IS_GNUCXX) @@ -32,24 +25,6 @@ find_package(Boost COMPONENTS program_options filesystem system log date_time th FIND_PACKAGE ( Threads REQUIRED ) include_directories(${Boost_INCLUDE_DIRS}) -# GDK-Pixbuf -find_package(GDK-Pixbuf REQUIRED) -include_directories(${GDK-PIXBUF_INCLUDE_DIRS}) - -#GDK -find_package(GDK REQUIRED) -include_directories(${GDK_INCLUDE_DIRS}) - -#GTK -find_package(GTK REQUIRED) -include_directories(${GTK_INCLUDE_DIRS}) - - -#GLib -set(GLIB_FIND_COMPONENTS gobject) -find_package(GLib REQUIRED) -include_directories(${GLIB_INCLUDE_DIRS}) - #Lua find_package(Lua 5.2 REQUIRED) include_directories(${LUA_INCLUDE_DIR}) @@ -83,14 +58,10 @@ set(hdrs add_executable(luacmdinterface ${src} ${hdrs}) target_link_libraries(luacmdinterface ${CMAKE_THREAD_LIBS_INIT} - ${CAIRO_LIBRARIES} ${PANGO_LIBRARIES} ${Boost_LIBRARIES} ${CURL_LIBRARIES} ${APR_LIBRARIES} - ${GLIB_GOBJECT_LIBRARIES} ${GLIB_LIBRARIES} ${LUA_LIBRARIES} - ${GDK-PIXBUF_LIBRARIES} - ${GTK_LIBRARIES} - lcluascript lckernel lcluascript lcviewernoqt ${Boost_LIBRARIES} + lcluascript lckernel lcluascript lcviewernoqt ${Boost_LIBRARIES} glfw ) diff --git a/luacmdinterface/main.cpp b/luacmdinterface/main.cpp index 5c48d353a..6f28ac6cc 100644 --- a/luacmdinterface/main.cpp +++ b/luacmdinterface/main.cpp @@ -4,16 +4,16 @@ #include #include -#include #include -#include #include -#include #include #include #include #include #include +#include +#include +#include namespace po = boost::program_options; @@ -25,7 +25,7 @@ static const int DEFAULT_IMAGE_WIDTH = 400; static const int DEFAULT_IMAGE_HEIGHT = 400; static std::string* readBuffer; -static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) { +static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void*) { size_t realsize = size * nmemb; readBuffer->append((char*) contents, realsize); return realsize; @@ -39,7 +39,6 @@ std::string loadFile(const std::string& url) { if (curl != nullptr) { curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - /* example.com is redirected, so we tell libcurl to follow redirection */ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); @@ -61,16 +60,6 @@ std::string loadFile(const std::string& url) { } } -std::ofstream* ofile; -cairo_status_t write_func (void* closure, const unsigned char* data, unsigned int length) { - - if (ofile->is_open()) { - ofile->write((const char*) data, length); - } - - return CAIRO_STATUS_SUCCESS; -} - static FILE* openFileDialog(bool isOpening, const char* description, const char* mode) { std::string path; @@ -102,7 +91,7 @@ int main(int argc, char** argv) { ("height,h", po::value(&height), "(optional) Set output image height, example -h 200") ("ifile,i", po::value(&fIn), "(required) Set LUA input file name, example: -i file:myFile.lua") ("ofile,o", po::value(&fOut), "(optional) Set output filename, example -o out.png") - ("otype,t", po::value(&fType), "(optional) output file type, example -t svg"); + ("otype,t", po::value(&fType), "(optional) output file type, example -t tga"); po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); @@ -130,7 +119,7 @@ int main(int argc, char** argv) { // Create Librecad document auto _storageManager = std::make_shared(); auto _document = std::make_shared(_storageManager); - auto _canvas = std::make_shared(_document); + auto _canvas = std::make_shared(_document); // Add background auto _gradientBackground = std::make_shared(lc::Color(0x90, 0x90, 0x90), @@ -144,24 +133,52 @@ int main(int argc, char** argv) { } std::transform(fType.begin(), fType.end(), fType.begin(), ::tolower); - ofile = new std::ofstream; - ofile->open(fOut); - using namespace CairoPainter; + LcPainter* lcPainter; + if(!glfwInit()) { + LOG_ERROR << "Failed to initialize GLFW"; + return -1; + } - LcPainter* lcPainter = nullptr; - if (fType == "pdf") { - lcPainter = new LcCairoPainter(width, height, &write_func); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + + GLFWwindow* window; + window = glfwCreateWindow(width, height, "LibreCAD", nullptr, nullptr); + if(window == nullptr) { + const char* description; + glfwGetError(&description); + LOG_ERROR << "Failed opening GLFW: " << description << std::endl; + glfwTerminate(); + return -1; } - else if (fType == "svg") { - lcPainter = new LcCairoPainter(width, height, &write_func); + + glfwMakeContextCurrent(window); + + glewExperimental = GL_TRUE; + GLenum err = glewInit(); + + if (err != GLEW_OK) { + LOG_ERROR << "GLEW Error: " << glewGetErrorString(err) << std::endl; + exit(1); } - else { - lcPainter = new LcCairoPainter(width, height, nullptr); + if (!GLEW_VERSION_2_1) { + LOG_ERROR << "OpenGL version 2.1 is not available" << std::endl; + exit(1); } + LOG_INFO << (char*) glGetString(GL_VERSION) << std::endl; + + lcPainter = lc::viewer::createOpenGLPainter(nullptr, width, height); + + lcPainter->create_resources(); + _canvas->setPainter(lcPainter); + // Set device width/height _canvas->newDeviceSize(width, height); + lcPainter->new_device_size(width, height); // Render Lua Code kaguya::State luaState; @@ -191,19 +208,31 @@ int main(int argc, char** argv) { } _canvas->autoScale(*lcPainter); - _canvas->render(*lcPainter, VIEWER_BACKGROUND); - _canvas->render(*lcPainter, VIEWER_DOCUMENT); - _canvas->render(*lcPainter, VIEWER_FOREGROUND); + lcPainter->clear(0,0,0); - if (fType == "png" || (fType != "pdf" && fType != "svg")) { - dynamic_cast*>(lcPainter)->writePNG(fOut); - } - ofile->close(); + _canvas->render(*lcPainter, lc::viewer::VIEWER_BACKGROUND); + _canvas->render(*lcPainter, lc::viewer::VIEWER_DOCUMENT); + _canvas->render(*lcPainter, lc::viewer::VIEWER_FOREGROUND); + + glfwSwapBuffers(window); + + FILE* out = fopen(fOut.c_str(), "wb"); + char* pixel_data = new char[3*width*height]; + short TGAhead[] = { 0, 2, 0, 0, 0, 0, static_cast(width), static_cast(height), 24 }; + + glReadBuffer(GL_FRONT); + glReadPixels(0, 0, width, height, GL_BGR, GL_UNSIGNED_BYTE, pixel_data); + + fwrite(&TGAhead,sizeof(TGAhead),1,out); + fwrite(pixel_data, 3*width*height, 1, out); + fclose(out); + + delete[] pixel_data; lc::lua::LuaCustomEntityManager::getInstance().removePlugins(); + glfwDestroyWindow(window); delete lcPainter; - delete ofile; delete readBuffer; return 0; } \ No newline at end of file diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index 5f18d7c5a..52e8ddb57 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -137,42 +137,17 @@ endif(WITH_QT_UI) if(WITH_RENDERING_UNITTESTS) add_compile_definitions(SOURCE_DIR=\"${CMAKE_CURRENT_LIST_DIR}\") - # GDK-Pixbuf - find_package(GDK-Pixbuf 2.30 REQUIRED) - include_directories(${GDK-PIXBUF_INCLUDE_DIRS}) - - #GDK - find_package(GDK REQUIRED) - include_directories(${GDK_INCLUDE_DIRS}) - - #GLib - set(GLIB_FIND_COMPONENTS gobject) - find_package(GLib REQUIRED) - include_directories(${GLIB_INCLUDE_DIRS}) - - # Cairo - find_package(Cairo 1.13 REQUIRED) - include_directories(${CAIRO_INCLUDE_DIRS}) - - # Pango - find_package(Pango 1.36 REQUIRED) - include_directories(${PANGO_INCLUDE_DIRS}) - link_directories(${PANGO_LIBRARIES}) - # Boost set(Boost_USE_MULTITHREADED ON) find_package(Boost COMPONENTS program_options filesystem system log REQUIRED) include_directories(${Boost_INCLUDE_DIRS}) + find_package(glfw3 3.2 REQUIRED) + set(EXTRA_LIBS ${EXTRA_LIBS} - ${CAIRO_LIBRARIES} - ${PANGO_LIBRARIES} - ${GDK-PIXBUF_LIBRARIES} - ${GDK_LIBRARIES} - ${GLIB_GOBJECT_LIBRARIES} - ${GLIB_LIBRARIES} ${Boost_LIBRARIES} + glfw ) set(src diff --git a/unittest/rendering/renderingtest.cpp b/unittest/rendering/renderingtest.cpp index 7ffc7bbbd..017e0331c 100644 --- a/unittest/rendering/renderingtest.cpp +++ b/unittest/rendering/renderingtest.cpp @@ -1,21 +1,23 @@ -#include +#include +#include #include + #ifndef WIN32 #include #endif -#include #include #include #include -#include #include #include #include #include #include #include +#include +#include #define DEFAULT_IMAGE_WIDTH 100 #define DEFAULT_IMAGE_HEIGHT 100 @@ -50,60 +52,101 @@ void render(const std::string& dxf, const std::string& output, unsigned int imag ); _canvas->background().connect(_gradientBackground.get()); - LcPainter* lcPainter = new LcCairoPainter(imageWidth, imageHeight, nullptr);; + lc::viewer::LcPainter* lcPainter; + if(!glfwInit()) { + LOG_ERROR << "Failed to initialize GLFW"; + return; + } + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + glfwWindowHint(GLFW_DOUBLEBUFFER, GL_FALSE); + + GLFWwindow* window; + window = glfwCreateWindow(imageWidth, imageHeight, "LibreCAD", nullptr, nullptr); + if(window == nullptr) { + glfwTerminate(); + FAIL() << "Failed opening GLFW: " << std::endl; + } + + glfwMakeContextCurrent(window); + + glewExperimental = GL_TRUE; + GLenum err = glewInit(); + + if (err != GLEW_OK) { + FAIL() << "GLEW Error: " << glewGetErrorString(err) << std::endl; + } + + LOG_INFO << (char*) glGetString(GL_VERSION) << std::endl; + + lcPainter = lc::viewer::createOpenGLPainter(nullptr, imageWidth, imageHeight); + lcPainter->create_resources(); + _canvas->setPainter(lcPainter); + + // Set device width/height _canvas->newDeviceSize(imageWidth, imageHeight); + lcPainter->new_device_size(imageWidth, imageHeight); + lc::persistence::File::open(_document, dxf, lc::persistence::File::LIBDXFRW); _canvas->setDisplayArea(*lcPainter, lc::geo::Area(lc::geo::Coordinate(x, y), w, h)); - _canvas->render(*lcPainter, VIEWER_BACKGROUND); - _canvas->render(*lcPainter, VIEWER_DOCUMENT); - _canvas->render(*lcPainter, VIEWER_FOREGROUND); + _canvas->render(*lcPainter, lc::viewer::VIEWER_BACKGROUND); + _canvas->render(*lcPainter, lc::viewer::VIEWER_DOCUMENT); + _canvas->render(*lcPainter, lc::viewer::VIEWER_FOREGROUND); + + glFinish(); - static_cast*>(lcPainter)->writePNG(output); + FILE* out = fopen(output.c_str(), "wb"); + GLubyte pixel_data[3*imageWidth*imageHeight]; + short TGAhead[] = { 0, 2, 0, 0, 0, 0, static_cast(imageWidth), static_cast(imageHeight), 24 }; + glReadPixels(0, 0, imageWidth, imageHeight, GL_RGB, GL_UNSIGNED_BYTE, pixel_data); + + fwrite(&TGAhead,sizeof(TGAhead),1,out); + fwrite(pixel_data, 3*imageWidth*imageHeight, 1, out); + fclose(out); + + glfwDestroyWindow(window); delete lcPainter; } bool checkRender(const std::string& image1, const std::string& image2, float tolerance) { - GError* error1 = NULL; - GError* error2 = NULL; - - auto pixbuf1 = gdk_pixbuf_new_from_file(image1.c_str(), &error1); - auto pixbuf2 = gdk_pixbuf_new_from_file(image2.c_str(), &error2); - - if(error1) { - std::cerr << error1->message << std::endl; - g_error_free(error1); - return false; - } - if(error2) { - std::cerr << error2->message << std::endl; - g_error_free(error2); + std::fstream f1; + f1.open(image1, std::ios::in); + if(f1.fail()) { + std::cerr << "File " << image1 << " can't be opened" << std::endl; return false; } - if(gdk_pixbuf_get_width(pixbuf1) != gdk_pixbuf_get_width(pixbuf2) || - gdk_pixbuf_get_height(pixbuf1) != gdk_pixbuf_get_height(pixbuf2) || - gdk_pixbuf_get_n_channels(pixbuf1) != gdk_pixbuf_get_n_channels(pixbuf2)) { + std::fstream f2; + f2.open(image2, std::ios::in); + if(f2.fail()) { + std::cerr << "File " << image2 << " can't be opened" << std::endl; return false; } auto channelTolerance = 256.0 * (tolerance / 100.0); - auto nbPixels = gdk_pixbuf_get_height(pixbuf1) * gdk_pixbuf_get_rowstride(pixbuf1); + char c1 = '0'; + char c2 = '0'; + while((c1 != EOF) && (c2 != EOF)){ + c1 = f1.get(); + c2 = f2.get(); - auto pixels1 = gdk_pixbuf_get_pixels(pixbuf1); - auto pixels2 = gdk_pixbuf_get_pixels(pixbuf2); + if(c1 < c2 - channelTolerance || c1 > c2 + channelTolerance){ + return false; + } - for(auto i = 0; i < nbPixels; i++) { - auto pixel1 = pixels1[i]; - auto pixel2 = pixels2[i]; - if(pixel1 < pixel2 - channelTolerance || pixel1 > pixel2 + channelTolerance) { - std::cerr << "Pixel " << i << " failed: " << (int) pixel1 << " - " << (int) pixel2 << " with tolerance " << channelTolerance << std::endl; + if((c1 == EOF) ^ (c2 == EOF)) { return false; } } + f1.close(); + f2.close(); return true; } @@ -142,7 +185,7 @@ TEST(RenderingTest, Test) { unsigned int testNumber = 0; bool dxfFound = false; - bool pngFound = false; + bool tgaFound = false; bool configFound = false; for(auto i = 0; i < nbFiles; i++) { @@ -156,23 +199,23 @@ TEST(RenderingTest, Test) { if(newNumber != testNumber) { testNumber = newNumber; dxfFound = false; - pngFound = false; + tgaFound = false; configFound = false; } if(strcmp(extension, "dxf") == 0) { dxfFound = true; } - else if(strcmp(extension, "png") == 0) { - pngFound = true; + else if(strcmp(extension, "tga") == 0) { + tgaFound = true; } else if(strcmp(extension, "cfg") == 0) { configFound = true; } - if(dxfFound && pngFound && configFound) { + if(dxfFound && tgaFound && configFound) { auto base = std::string(resDir) + std::to_string(newNumber); - auto expectedFile = base + ".png"; + auto expectedFile = base + ".tga"; auto dxfFile = base + ".dxf"; auto resultFile = base + ".out"; auto configFile = base + ".cfg"; @@ -193,7 +236,7 @@ TEST(RenderingTest, Test) { ASSERT_TRUE(checkRender(expectedFile, resultFile, tolerance)) << "Failed with " << expectedFile; dxfFound = false; //Prevent running the test more than once - pngFound = false; + tgaFound = false; configFound = false; } } diff --git a/unittest/rendering/res/1.png b/unittest/rendering/res/1.png deleted file mode 100644 index f2bcf851f..000000000 Binary files a/unittest/rendering/res/1.png and /dev/null differ diff --git a/unittest/rendering/res/1.tga b/unittest/rendering/res/1.tga new file mode 100644 index 000000000..397a185e2 Binary files /dev/null and b/unittest/rendering/res/1.tga differ diff --git a/unittest/rendering/res/2.png b/unittest/rendering/res/2.png deleted file mode 100644 index 2fc83fabb..000000000 Binary files a/unittest/rendering/res/2.png and /dev/null differ diff --git a/unittest/rendering/res/2.tga b/unittest/rendering/res/2.tga new file mode 100644 index 000000000..8646dae0b Binary files /dev/null and b/unittest/rendering/res/2.tga differ