diff options
Diffstat (limited to 'indra/integration_tests/llimage_libtest')
3 files changed, 591 insertions, 0 deletions
diff --git a/indra/integration_tests/llimage_libtest/CMakeLists.txt b/indra/integration_tests/llimage_libtest/CMakeLists.txt new file mode 100644 index 0000000000..a687e60610 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/CMakeLists.txt @@ -0,0 +1,125 @@ +# -*- cmake -*- + +# Integration tests of the llimage library (JPEG2000, PNG, jpeg, etc... images reading and writing) + +project (llimage_libtest) + +include(00-Common) +include(LLCommon) +include(LLImage) +include(LLImageJ2COJ) +include(LLKDU) +include(LLVFS) + +include_directories( + ${LLCOMMON_INCLUDE_DIRS} + ${LLVFS_INCLUDE_DIRS} + ${LLIMAGE_INCLUDE_DIRS} + ) + +set(llimage_libtest_SOURCE_FILES + llimage_libtest.cpp + ) + +set(llimage_libtest_HEADER_FILES + CMakeLists.txt + llimage_libtest.h + ) + +set_source_files_properties(${llimage_libtest_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND llimage_libtest_SOURCE_FILES ${llimage_libtest_HEADER_FILES}) + +add_executable(llimage_libtest + WIN32 + MACOSX_BUNDLE + ${llimage_libtest_SOURCE_FILES} +) + +set_target_properties(llimage_libtest + PROPERTIES + WIN32_EXECUTABLE + FALSE +) + +# OS-specific libraries +if (DARWIN) + include(CMakeFindFrameworks) + find_library(COREFOUNDATION_LIBRARY CoreFoundation) + set(OS_LIBRARIES ${COREFOUNDATION_LIBRARY}) +elseif (WINDOWS) + set(OS_LIBRARIES) +elseif (LINUX) + set(OS_LIBRARIES) +else (DARWIN) + message(FATAL_ERROR "Unknown platform") +endif (DARWIN) + +# Libraries on which this application depends on +# Sort by high-level to low-level +target_link_libraries(llimage_libtest + ${LLCOMMON_LIBRARIES} + ${LLVFS_LIBRARIES} + ${LLIMAGE_LIBRARIES} + ${LLKDU_LIBRARIES} + ${KDU_LIBRARY} + ${LLIMAGEJ2COJ_LIBRARIES} + ${OS_LIBRARIES} + ) + +if (DARWIN) + # Path inside the app bundle where we'll need to copy libraries + set(LLIMAGE_LIBTEST_DESTINATION_DIR + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/llimage_libtest.app/Contents/Resources + ) + # Create the Contents/Resources directory + add_custom_command( + TARGET llimage_libtest POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS + -E + make_directory + ${LLIMAGE_LIBTEST_DESTINATION_DIR} + COMMENT "Creating Resources directory in app bundle." + ) +else (DARWIN) + set(LLIMAGE_LIBTEST_DESTINATION_DIR + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + ) +endif (DARWIN) + +get_target_property(BUILT_LLCOMMON llcommon LOCATION) +add_custom_command(TARGET llimage_libtest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_LLCOMMON} ${LLIMAGE_LIBTEST_DESTINATION_DIR} + DEPENDS ${BUILT_LLCOMMON} +) + +if (DARWIN) + # Copy the required libraries to the package app + add_custom_command(TARGET llimage_libtest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../build-darwin-i386/packages/lib/release/libapr-1.0.dylib ${LLIMAGE_LIBTEST_DESTINATION_DIR} + DEPENDS ${CMAKE_SOURCE_DIR}/../build-darwin-i386/packages/lib/release/libapr-1.0.dylib + ) + add_custom_command(TARGET llimage_libtest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../build-darwin-i386/packages/lib/release/libaprutil-1.0.dylib ${LLIMAGE_LIBTEST_DESTINATION_DIR} + DEPENDS ${CMAKE_SOURCE_DIR}/../build-darwin-i386/packages/lib/release/libaprutil-1.0.dylib + ) + add_custom_command(TARGET llimage_libtest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../build-darwin-i386/packages/lib/release/libexception_handler.dylib ${LLIMAGE_LIBTEST_DESTINATION_DIR} + DEPENDS ${CMAKE_SOURCE_DIR}/../build-darwin-i386/packages/lib/release/libexception_handler.dylib + ) + add_custom_command(TARGET llimage_libtest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../build-darwin-i386/packages/lib/release/libexpat.1.5.2.dylib ${LLIMAGE_LIBTEST_DESTINATION_DIR} + DEPENDS ${CMAKE_SOURCE_DIR}/../build-darwin-i386/packages/lib/release/libexpat.1.5.2.dylib + ) +endif (DARWIN) + +if (WINDOWS) + # Check indra/test_apps/llplugintest/CMakeLists.txt for an example of what to copy over for Windows and how +endif (WINDOWS) + +# Ensure people working on the viewer don't break this library +# *NOTE: This could be removed, or only built by TeamCity, if the build +# and link times become too long. +add_dependencies(viewer llimage_libtest) diff --git a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp new file mode 100644 index 0000000000..365f5f758c --- /dev/null +++ b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp @@ -0,0 +1,437 @@ +/** + * @file llimage_libtest.cpp + * @author Merov Linden + * @brief Integration test for the llimage library + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#include "linden_common.h" +#include "llpointer.h" +#include "lltimer.h" + +#include "llimage_libtest.h" + +// Linden library includes +#include "llimage.h" +#include "llimagejpeg.h" +#include "llimagepng.h" +#include "llimagebmp.h" +#include "llimagetga.h" +#include "llimagej2c.h" +#include "lldir.h" + +// system libraries +#include <iostream> + +// doc string provided when invoking the program with --help +static const char USAGE[] = "\n" +"usage:\tllimage_libtest [options]\n" +"\n" +" -h, --help\n" +" Print this help\n" +" -i, --input <file1 .. file2>\n" +" List of image files to load and convert. Patterns with wild cards can be used.\n" +" -o, --output <file1 .. file2> OR <type>\n" +" List of image files to create (assumes same order as for input files)\n" +" OR 3 letters file type extension to convert each input file into.\n" +" -log, --logmetrics <metric>\n" +" Log performance data for <metric>. Results in <metric>.slp\n" +" Note: so far, only ImageCompressionTester has been tested.\n" +" -r, --analyzeperformance\n" +" Create a report comparing <metric>_baseline.slp with current <metric>.slp\n" +" Results in <metric>_report.csv" +" -s, --image-stats\n" +" Output stats for each input and output image.\n" +"\n"; + +// true when all image loading is done. Used by metric logging thread to know when to stop the thread. +static bool sAllDone = false; + +// Create an empty formatted image instance of the correct type from the filename +LLPointer<LLImageFormatted> create_image(const std::string &filename) +{ + std::string exten = gDirUtilp->getExtension(filename); + U32 codec = LLImageBase::getCodecFromExtension(exten); + + LLPointer<LLImageFormatted> image; + switch (codec) + { + case IMG_CODEC_BMP: + image = new LLImageBMP(); + break; + case IMG_CODEC_TGA: + image = new LLImageTGA(); + break; + case IMG_CODEC_JPEG: + image = new LLImageJPEG(); + break; + case IMG_CODEC_J2C: + image = new LLImageJ2C(); + break; + case IMG_CODEC_PNG: + image = new LLImagePNG(); + break; + default: + return NULL; + } + + return image; +} + +void output_image_stats(LLPointer<LLImageFormatted> image, const std::string &filename) +{ + // Print out some statistical data on the image + std::cout << "Image stats for : " << filename << ", extension : " << image->getExtension() << std::endl; + + std::cout << " with : " << (int)(image->getWidth()) << ", height : " << (int)(image->getHeight()) << std::endl; + std::cout << " comp : " << (int)(image->getComponents()) << ", levels : " << (int)(image->getDiscardLevel()) << std::endl; + std::cout << " head : " << (int)(image->calcHeaderSize()) << ", data : " << (int)(image->getDataSize()) << std::endl; + + return; +} + +// Load an image from file and return a raw (decompressed) instance of its data +LLPointer<LLImageRaw> load_image(const std::string &src_filename, bool output_stats) +{ + LLPointer<LLImageFormatted> image = create_image(src_filename); + + if (!image->load(src_filename)) + { + return NULL; + } + + if( (image->getComponents() != 3) && (image->getComponents() != 4) ) + { + std::cout << "Image files with less than 3 or more than 4 components are not supported\n"; + return NULL; + } + + if (output_stats) + { + output_image_stats(image, src_filename); + } + + LLPointer<LLImageRaw> raw_image = new LLImageRaw; + if (!image->decode(raw_image, 0.0f)) + { + return NULL; + } + + return raw_image; +} + +// Save a raw image instance into a file +bool save_image(const std::string &dest_filename, LLPointer<LLImageRaw> raw_image, bool output_stats) +{ + LLPointer<LLImageFormatted> image = create_image(dest_filename); + + if (!image->encode(raw_image, 0.0f)) + { + return false; + } + + if (output_stats) + { + output_image_stats(image, dest_filename); + } + + return image->save(dest_filename); +} + +void store_input_file(std::list<std::string> &input_filenames, const std::string &path) +{ + // Break the incoming path in its components + std::string dir = gDirUtilp->getDirName(path); + std::string name = gDirUtilp->getBaseFileName(path); + std::string exten = gDirUtilp->getExtension(path); + + // std::cout << "store_input_file : " << path << ", dir : " << dir << ", name : " << name << ", exten : " << exten << std::endl; + + // If extension is not an image type or "*", exit + // Note: we don't support complex patterns for the extension like "j??" + // Note: on most shells, the pattern expansion is done by the shell so that pattern matching limitation is actually not a problem + if ((exten.compare("*") != 0) && (LLImageBase::getCodecFromExtension(exten) == IMG_CODEC_INVALID)) + { + return; + } + + if ((name.find('*') != -1) || ((name.find('?') != -1))) + { + // If file name is a pattern, iterate to get each file name and store + std::string next_name; + while (gDirUtilp->getNextFileInDir(dir,name,next_name)) + { + std::string file_name = dir + gDirUtilp->getDirDelimiter() + next_name; + input_filenames.push_back(file_name); + } + } + else + { + // Verify that the file does exist before storing + if (gDirUtilp->fileExists(path)) + { + input_filenames.push_back(path); + } + else + { + std::cout << "store_input_file : the file " << path << " could not be found" << std::endl; + } + } +} + +void store_output_file(std::list<std::string> &output_filenames, std::list<std::string> &input_filenames, const std::string &path) +{ + // Break the incoming path in its components + std::string dir = gDirUtilp->getDirName(path); + std::string name = gDirUtilp->getBaseFileName(path); + std::string exten = gDirUtilp->getExtension(path); + + // std::cout << "store_output_file : " << path << ", dir : " << dir << ", name : " << name << ", exten : " << exten << std::endl; + + if (dir.empty() && exten.empty()) + { + // If dir and exten are empty, we interpret the name as a file extension type name and will iterate through input list to populate the output list + exten = name; + // Make sure the extension is an image type + if (LLImageBase::getCodecFromExtension(exten) == IMG_CODEC_INVALID) + { + return; + } + std::string delim = gDirUtilp->getDirDelimiter(); + std::list<std::string>::iterator in_file = input_filenames.begin(); + std::list<std::string>::iterator end = input_filenames.end(); + for (; in_file != end; ++in_file) + { + dir = gDirUtilp->getDirName(*in_file); + name = gDirUtilp->getBaseFileName(*in_file,true); + std::string file_name; + if (!dir.empty()) + { + file_name = dir + delim + name + "." + exten; + } + else + { + file_name = name + "." + exten; + } + output_filenames.push_back(file_name); + } + } + else + { + // Make sure the extension is an image type + if (LLImageBase::getCodecFromExtension(exten) == IMG_CODEC_INVALID) + { + return; + } + // Store the path + output_filenames.push_back(path); + } +} + +// Holds the metric gathering output in a thread safe way +class LogThread : public LLThread +{ +public: + std::string mFile; + + LogThread(std::string& test_name) : LLThread("llimage_libtest log") + { + std::string file_name = test_name + std::string(".slp"); + mFile = file_name; + } + + void run() + { + std::ofstream os(mFile.c_str()); + + while (!sAllDone) + { + LLFastTimer::writeLog(os); + os.flush(); + ms_sleep(32); + } + LLFastTimer::writeLog(os); + os.flush(); + os.close(); + } +}; + +int main(int argc, char** argv) +{ + // List of input and output files + std::list<std::string> input_filenames; + std::list<std::string> output_filenames; + bool analyze_performance = false; + bool image_stats = false; + + // Init whatever is necessary + ll_init_apr(); + LLImage::initClass(); + LogThread* fast_timer_log_thread = NULL; // For performance and metric gathering + + // Analyze command line arguments + for (int arg = 1; arg < argc; ++arg) + { + if (!strcmp(argv[arg], "--help") || !strcmp(argv[arg], "-h")) + { + // Send the usage to standard out + std::cout << USAGE << std::endl; + return 0; + } + else if ((!strcmp(argv[arg], "--input") || !strcmp(argv[arg], "-i")) && arg < argc-1) + { + std::string file_name = argv[arg+1]; + while (file_name[0] != '-') // if arg starts with '-', we consider it's not a file name but some other argument + { + // std::cout << "input file name : " << file_name << std::endl; + store_input_file(input_filenames, file_name); + arg += 1; // Skip that arg now we know it's a file name + if ((arg + 1) == argc) // Break out of the loop if we reach the end of the arg list + break; + file_name = argv[arg+1]; // Next argument and loop over + } + } + else if ((!strcmp(argv[arg], "--output") || !strcmp(argv[arg], "-o")) && arg < argc-1) + { + std::string file_name = argv[arg+1]; + while (file_name[0] != '-') // if arg starts with '-', we consider it's not a file name but some other argument + { + // std::cout << "output file name : " << file_name << std::endl; + store_output_file(output_filenames, input_filenames, file_name); + arg += 1; // Skip that arg now we know it's a file name + if ((arg + 1) == argc) // Break out of the loop if we reach the end of the arg list + break; + file_name = argv[arg+1]; // Next argument and loop over + } + } + else if (!strcmp(argv[arg], "--logmetrics") || !strcmp(argv[arg], "-log")) + { + // '--logmetrics' needs to be specified with a named test metric argument + // Note: for the moment, only ImageCompressionTester has been tested + std::string test_name; + if ((arg + 1) < argc) + { + test_name = argv[arg+1]; + } + if (((arg + 1) >= argc) || (test_name[0] == '-')) + { + // We don't have an argument left in the arg list or the next argument is another option + std::cout << "No --logmetrics argument given, no perf data will be gathered" << std::endl; + } + else + { + LLFastTimer::sMetricLog = TRUE; + LLFastTimer::sLogName = test_name; + arg += 1; // Skip that arg now we know it's a valid test name + if ((arg + 1) == argc) // Break out of the loop if we reach the end of the arg list + break; + } + } + else if (!strcmp(argv[arg], "--analyzeperformance") || !strcmp(argv[arg], "-r")) + { + analyze_performance = true; + } + else if (!strcmp(argv[arg], "--image-stats") || !strcmp(argv[arg], "-s")) + { + image_stats = true; + } + } + + // Check arguments consistency. Exit with proper message if inconsistent. + if (input_filenames.size() == 0) + { + std::cout << "No input file, nothing to do -> exit" << std::endl; + return 0; + } + if (analyze_performance && !LLFastTimer::sMetricLog) + { + std::cout << "Cannot create perf report if no perf gathered (i.e. use argument -log <perf> with -r) -> exit" << std::endl; + return 0; + } + + + // Create the logging thread if required + if (LLFastTimer::sMetricLog) + { + LLFastTimer::sLogLock = new LLMutex(NULL); + fast_timer_log_thread = new LogThread(LLFastTimer::sLogName); + fast_timer_log_thread->start(); + } + + // Perform action on each input file + std::list<std::string>::iterator in_file = input_filenames.begin(); + std::list<std::string>::iterator out_file = output_filenames.begin(); + std::list<std::string>::iterator in_end = input_filenames.end(); + std::list<std::string>::iterator out_end = output_filenames.end(); + for (; in_file != in_end; ++in_file) + { + // Load file + LLPointer<LLImageRaw> raw_image = load_image(*in_file, image_stats); + if (!raw_image) + { + std::cout << "Error: Image " << *in_file << " could not be loaded" << std::endl; + continue; + } + + // Save file + if (out_file != out_end) + { + if (!save_image(*out_file, raw_image, image_stats)) + { + std::cout << "Error: Image " << *out_file << " could not be saved" << std::endl; + } + else + { + std::cout << *in_file << " -> " << *out_file << std::endl; + } + ++out_file; + } + } + + // Stop the perf gathering system if needed + if (LLFastTimer::sMetricLog) + { + LLMetricPerformanceTesterBasic::deleteTester(LLFastTimer::sLogName); + sAllDone = true; + } + + // Output perf data if requested by user + if (analyze_performance) + { + std::cout << "Analyzing performance" << std::endl; + + std::string baseline_name = LLFastTimer::sLogName + "_baseline.slp"; + std::string current_name = LLFastTimer::sLogName + ".slp"; + std::string report_name = LLFastTimer::sLogName + "_report.csv"; + + LLMetricPerformanceTesterBasic::doAnalysisMetrics(baseline_name, current_name, report_name); + } + + // Cleanup and exit + LLImage::cleanupClass(); + if (fast_timer_log_thread) + { + fast_timer_log_thread->shutdown(); + } + + return 0; +} diff --git a/indra/integration_tests/llimage_libtest/llimage_libtest.h b/indra/integration_tests/llimage_libtest/llimage_libtest.h new file mode 100644 index 0000000000..63f3d46b50 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/llimage_libtest.h @@ -0,0 +1,29 @@ +/** + * @file llimage_libtest.h + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef LLIMAGE_LIBTEST_H +#define LLIMAGE_LIBTEST_H + + +#endif |