PageRenderTime 29ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/integration_tests/llimage_libtest/llimage_libtest.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 561 lines | 440 code | 47 blank | 74 comment | 134 complexity | bfe2a4e54ea646f9affbac5f9017e9a3 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llimage_libtest.cpp
  3. * @author Merov Linden
  4. * @brief Integration test for the llimage library
  5. *
  6. * $LicenseInfo:firstyear=2011&license=viewerlgpl$
  7. * Second Life Viewer Source Code
  8. * Copyright (C) 2011, Linden Research, Inc.
  9. *
  10. * This library is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU Lesser General Public
  12. * License as published by the Free Software Foundation;
  13. * version 2.1 of the License only.
  14. *
  15. * This library is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  18. * Lesser General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Lesser General Public
  21. * License along with this library; if not, write to the Free Software
  22. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  23. *
  24. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  25. * $/LicenseInfo$
  26. */
  27. #include "linden_common.h"
  28. #include "llpointer.h"
  29. #include "lltimer.h"
  30. #include "llimage_libtest.h"
  31. // Linden library includes
  32. #include "llimage.h"
  33. #include "llimagejpeg.h"
  34. #include "llimagepng.h"
  35. #include "llimagebmp.h"
  36. #include "llimagetga.h"
  37. #include "llimagej2c.h"
  38. #include "lldir.h"
  39. #include "lldiriterator.h"
  40. // system libraries
  41. #include <iostream>
  42. // doc string provided when invoking the program with --help
  43. static const char USAGE[] = "\n"
  44. "usage:\tllimage_libtest [options]\n"
  45. "\n"
  46. " -h, --help\n"
  47. " Print this help\n"
  48. " -i, --input <file1 .. file2>\n"
  49. " List of image files to load and convert. Patterns with wild cards can be used.\n"
  50. " -o, --output <file1 .. file2> OR <type>\n"
  51. " List of image files to create (assumes same order as for input files)\n"
  52. " OR 3 letters file type extension to convert each input file into.\n"
  53. " -r, --region <x0, y0, x1, y1>\n"
  54. " Crop region applied to the input files in pixels.\n"
  55. " Only used for j2c images. Default is no region cropping.\n"
  56. " -d, --discard_level <n>\n"
  57. " Discard level max used on input. 0 is highest resolution. Max discard level is 5.\n"
  58. " This allows the input image to be clamped in resolution when loading.\n"
  59. " Only valid for j2c images. Default is no discard.\n"
  60. " -p, --precincts <n>\n"
  61. " Dimension of precincts in pixels. Precincts are assumed square and identical for\n"
  62. " all levels. Note that this option also add PLT and tile markers to the codestream, \n"
  63. " and uses RPCL order. Power of 2 must be used.\n"
  64. " Only valid for output j2c images. Default is no precincts used.\n"
  65. " -b, --blocks <n>\n"
  66. " Dimension of coding blocks in pixels. Blocks are assumed square. Power of 2 must\n"
  67. " be used. Blocks must be smaller than precincts. Like precincts, this option adds\n"
  68. " PLT, tile markers and uses RPCL.\n"
  69. " Only valid for output j2c images. Default is 64.\n"
  70. " -l, --levels <n>\n"
  71. " Number of decomposition levels (aka discard levels) in the output image.\n"
  72. " The maximum number of levels authorized is 32.\n"
  73. " Only valid for output j2c images. Default is 5.\n"
  74. " -rev, --reversible\n"
  75. " Set the compression to be lossless (reversible in j2c parlance).\n"
  76. " Only valid for output j2c images.\n"
  77. " -log, --logmetrics <metric>\n"
  78. " Log performance data for <metric>. Results in <metric>.slp\n"
  79. " Note: so far, only ImageCompressionTester has been tested.\n"
  80. " -a, --analyzeperformance\n"
  81. " Create a report comparing <metric>_baseline.slp with current <metric>.slp\n"
  82. " Results in <metric>_report.csv\n"
  83. " -s, --image-stats\n"
  84. " Output stats for each input and output image.\n"
  85. "\n";
  86. // true when all image loading is done. Used by metric logging thread to know when to stop the thread.
  87. static bool sAllDone = false;
  88. // Create an empty formatted image instance of the correct type from the filename
  89. LLPointer<LLImageFormatted> create_image(const std::string &filename)
  90. {
  91. std::string exten = gDirUtilp->getExtension(filename);
  92. LLPointer<LLImageFormatted> image = LLImageFormatted::createFromExtension(exten);
  93. return image;
  94. }
  95. void output_image_stats(LLPointer<LLImageFormatted> image, const std::string &filename)
  96. {
  97. // Print out some statistical data on the image
  98. std::cout << "Image stats for : " << filename << ", extension : " << image->getExtension() << std::endl;
  99. std::cout << " with : " << (int)(image->getWidth()) << ", height : " << (int)(image->getHeight()) << std::endl;
  100. std::cout << " comp : " << (int)(image->getComponents()) << ", levels : " << (int)(image->getDiscardLevel()) << std::endl;
  101. std::cout << " head : " << (int)(image->calcHeaderSize()) << ", data : " << (int)(image->getDataSize()) << std::endl;
  102. return;
  103. }
  104. // Load an image from file and return a raw (decompressed) instance of its data
  105. LLPointer<LLImageRaw> load_image(const std::string &src_filename, int discard_level, int* region, bool output_stats)
  106. {
  107. LLPointer<LLImageFormatted> image = create_image(src_filename);
  108. // This just loads the image file stream into a buffer. No decoding done.
  109. if (!image->load(src_filename))
  110. {
  111. return NULL;
  112. }
  113. if( (image->getComponents() != 3) && (image->getComponents() != 4) )
  114. {
  115. std::cout << "Image files with less than 3 or more than 4 components are not supported\n";
  116. return NULL;
  117. }
  118. if (output_stats)
  119. {
  120. output_image_stats(image, src_filename);
  121. }
  122. LLPointer<LLImageRaw> raw_image = new LLImageRaw;
  123. // Set the image restriction on load in the case of a j2c image
  124. if ((image->getCodec() == IMG_CODEC_J2C) && ((discard_level != -1) || (region != NULL)))
  125. {
  126. // That method doesn't exist (and likely, doesn't make sense) for any other image file format
  127. // hence the required cryptic cast.
  128. ((LLImageJ2C*)(image.get()))->initDecode(*raw_image, discard_level, region);
  129. }
  130. if (!image->decode(raw_image, 0.0f))
  131. {
  132. return NULL;
  133. }
  134. return raw_image;
  135. }
  136. // Save a raw image instance into a file
  137. bool save_image(const std::string &dest_filename, LLPointer<LLImageRaw> raw_image, int blocks_size, int precincts_size, int levels, bool reversible, bool output_stats)
  138. {
  139. LLPointer<LLImageFormatted> image = create_image(dest_filename);
  140. // Set the image codestream parameters on output in the case of a j2c image
  141. if (image->getCodec() == IMG_CODEC_J2C)
  142. {
  143. // That method doesn't exist (and likely, doesn't make sense) for any other image file format
  144. // hence the required cryptic cast.
  145. if ((blocks_size != -1) || (precincts_size != -1) || (levels != 0))
  146. {
  147. ((LLImageJ2C*)(image.get()))->initEncode(*raw_image, blocks_size, precincts_size, levels);
  148. }
  149. ((LLImageJ2C*)(image.get()))->setReversible(reversible);
  150. }
  151. if (!image->encode(raw_image, 0.0f))
  152. {
  153. return false;
  154. }
  155. if (output_stats)
  156. {
  157. output_image_stats(image, dest_filename);
  158. }
  159. return image->save(dest_filename);
  160. }
  161. void store_input_file(std::list<std::string> &input_filenames, const std::string &path)
  162. {
  163. // Break the incoming path in its components
  164. std::string dir = gDirUtilp->getDirName(path);
  165. std::string name = gDirUtilp->getBaseFileName(path);
  166. std::string exten = gDirUtilp->getExtension(path);
  167. // std::cout << "store_input_file : " << path << ", dir : " << dir << ", name : " << name << ", exten : " << exten << std::endl;
  168. // If extension is not an image type or "*", exit
  169. // Note: we don't support complex patterns for the extension like "j??"
  170. // Note: on most shells, the pattern expansion is done by the shell so that pattern matching limitation is actually not a problem
  171. if ((exten.compare("*") != 0) && (LLImageBase::getCodecFromExtension(exten) == IMG_CODEC_INVALID))
  172. {
  173. return;
  174. }
  175. if ((name.find('*') != -1) || ((name.find('?') != -1)))
  176. {
  177. // If file name is a pattern, iterate to get each file name and store
  178. std::string next_name;
  179. LLDirIterator iter(dir, name);
  180. while (iter.next(next_name))
  181. {
  182. std::string file_name = dir + gDirUtilp->getDirDelimiter() + next_name;
  183. input_filenames.push_back(file_name);
  184. }
  185. }
  186. else
  187. {
  188. // Verify that the file does exist before storing
  189. if (gDirUtilp->fileExists(path))
  190. {
  191. input_filenames.push_back(path);
  192. }
  193. else
  194. {
  195. std::cout << "store_input_file : the file " << path << " could not be found" << std::endl;
  196. }
  197. }
  198. }
  199. void store_output_file(std::list<std::string> &output_filenames, std::list<std::string> &input_filenames, const std::string &path)
  200. {
  201. // Break the incoming path in its components
  202. std::string dir = gDirUtilp->getDirName(path);
  203. std::string name = gDirUtilp->getBaseFileName(path);
  204. std::string exten = gDirUtilp->getExtension(path);
  205. // std::cout << "store_output_file : " << path << ", dir : " << dir << ", name : " << name << ", exten : " << exten << std::endl;
  206. if (dir.empty() && exten.empty())
  207. {
  208. // 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
  209. exten = name;
  210. // Make sure the extension is an image type
  211. if (LLImageBase::getCodecFromExtension(exten) == IMG_CODEC_INVALID)
  212. {
  213. return;
  214. }
  215. std::string delim = gDirUtilp->getDirDelimiter();
  216. std::list<std::string>::iterator in_file = input_filenames.begin();
  217. std::list<std::string>::iterator end = input_filenames.end();
  218. for (; in_file != end; ++in_file)
  219. {
  220. dir = gDirUtilp->getDirName(*in_file);
  221. name = gDirUtilp->getBaseFileName(*in_file,true);
  222. std::string file_name;
  223. if (!dir.empty())
  224. {
  225. file_name = dir + delim + name + "." + exten;
  226. }
  227. else
  228. {
  229. file_name = name + "." + exten;
  230. }
  231. output_filenames.push_back(file_name);
  232. }
  233. }
  234. else
  235. {
  236. // Make sure the extension is an image type
  237. if (LLImageBase::getCodecFromExtension(exten) == IMG_CODEC_INVALID)
  238. {
  239. return;
  240. }
  241. // Store the path
  242. output_filenames.push_back(path);
  243. }
  244. }
  245. // Holds the metric gathering output in a thread safe way
  246. class LogThread : public LLThread
  247. {
  248. public:
  249. std::string mFile;
  250. LogThread(std::string& test_name) : LLThread("llimage_libtest log")
  251. {
  252. std::string file_name = test_name + std::string(".slp");
  253. mFile = file_name;
  254. }
  255. void run()
  256. {
  257. std::ofstream os(mFile.c_str());
  258. while (!sAllDone)
  259. {
  260. LLFastTimer::writeLog(os);
  261. os.flush();
  262. ms_sleep(32);
  263. }
  264. LLFastTimer::writeLog(os);
  265. os.flush();
  266. os.close();
  267. }
  268. };
  269. int main(int argc, char** argv)
  270. {
  271. // List of input and output files
  272. std::list<std::string> input_filenames;
  273. std::list<std::string> output_filenames;
  274. // Other optional parsed arguments
  275. bool analyze_performance = false;
  276. bool image_stats = false;
  277. int* region = NULL;
  278. int discard_level = -1;
  279. int precincts_size = -1;
  280. int blocks_size = -1;
  281. int levels = 0;
  282. bool reversible = false;
  283. // Init whatever is necessary
  284. ll_init_apr();
  285. LLImage::initClass();
  286. LogThread* fast_timer_log_thread = NULL; // For performance and metric gathering
  287. // Analyze command line arguments
  288. for (int arg = 1; arg < argc; ++arg)
  289. {
  290. if (!strcmp(argv[arg], "--help") || !strcmp(argv[arg], "-h"))
  291. {
  292. // Send the usage to standard out
  293. std::cout << USAGE << std::endl;
  294. return 0;
  295. }
  296. else if ((!strcmp(argv[arg], "--input") || !strcmp(argv[arg], "-i")) && arg < argc-1)
  297. {
  298. std::string file_name = argv[arg+1];
  299. while (file_name[0] != '-') // if arg starts with '-', we consider it's not a file name but some other argument
  300. {
  301. // std::cout << "input file name : " << file_name << std::endl;
  302. store_input_file(input_filenames, file_name);
  303. arg += 1; // Skip that arg now we know it's a file name
  304. if ((arg + 1) == argc) // Break out of the loop if we reach the end of the arg list
  305. break;
  306. file_name = argv[arg+1]; // Next argument and loop over
  307. }
  308. }
  309. else if ((!strcmp(argv[arg], "--output") || !strcmp(argv[arg], "-o")) && arg < argc-1)
  310. {
  311. std::string file_name = argv[arg+1];
  312. while (file_name[0] != '-') // if arg starts with '-', we consider it's not a file name but some other argument
  313. {
  314. // std::cout << "output file name : " << file_name << std::endl;
  315. store_output_file(output_filenames, input_filenames, file_name);
  316. arg += 1; // Skip that arg now we know it's a file name
  317. if ((arg + 1) == argc) // Break out of the loop if we reach the end of the arg list
  318. break;
  319. file_name = argv[arg+1]; // Next argument and loop over
  320. }
  321. }
  322. else if ((!strcmp(argv[arg], "--region") || !strcmp(argv[arg], "-r")) && arg < argc-1)
  323. {
  324. std::string value_str = argv[arg+1];
  325. int index = 0;
  326. region = new int[4];
  327. while (value_str[0] != '-') // if arg starts with '-', it's the next option
  328. {
  329. int value = atoi(value_str.c_str());
  330. region[index++] = value;
  331. arg += 1; // Definitely skip that arg now we know it's a number
  332. if ((arg + 1) == argc) // Break out of the loop if we reach the end of the arg list
  333. break;
  334. if (index == 4) // Break out of the loop if we captured 4 values already
  335. break;
  336. value_str = argv[arg+1]; // Next argument and loop over
  337. }
  338. if (index != 4)
  339. {
  340. std::cout << "--region arguments invalid" << std::endl;
  341. delete [] region;
  342. region = NULL;
  343. }
  344. }
  345. else if (!strcmp(argv[arg], "--discard_level") || !strcmp(argv[arg], "-d"))
  346. {
  347. std::string value_str;
  348. if ((arg + 1) < argc)
  349. {
  350. value_str = argv[arg+1];
  351. }
  352. if (((arg + 1) >= argc) || (value_str[0] == '-'))
  353. {
  354. std::cout << "No valid --discard_level argument given, discard_level ignored" << std::endl;
  355. }
  356. else
  357. {
  358. discard_level = atoi(value_str.c_str());
  359. // Clamp to the values accepted by the viewer
  360. discard_level = llclamp(discard_level,0,5);
  361. }
  362. }
  363. else if (!strcmp(argv[arg], "--precincts") || !strcmp(argv[arg], "-p"))
  364. {
  365. std::string value_str;
  366. if ((arg + 1) < argc)
  367. {
  368. value_str = argv[arg+1];
  369. }
  370. if (((arg + 1) >= argc) || (value_str[0] == '-'))
  371. {
  372. std::cout << "No valid --precincts argument given, precincts ignored" << std::endl;
  373. }
  374. else
  375. {
  376. precincts_size = atoi(value_str.c_str());
  377. }
  378. }
  379. else if (!strcmp(argv[arg], "--blocks") || !strcmp(argv[arg], "-b"))
  380. {
  381. std::string value_str;
  382. if ((arg + 1) < argc)
  383. {
  384. value_str = argv[arg+1];
  385. }
  386. if (((arg + 1) >= argc) || (value_str[0] == '-'))
  387. {
  388. std::cout << "No valid --blocks argument given, blocks ignored" << std::endl;
  389. }
  390. else
  391. {
  392. blocks_size = atoi(value_str.c_str());
  393. }
  394. }
  395. else if (!strcmp(argv[arg], "--levels") || !strcmp(argv[arg], "-l"))
  396. {
  397. std::string value_str;
  398. if ((arg + 1) < argc)
  399. {
  400. value_str = argv[arg+1];
  401. }
  402. if (((arg + 1) >= argc) || (value_str[0] == '-'))
  403. {
  404. std::cout << "No valid --levels argument given, default (5) will be used" << std::endl;
  405. }
  406. else
  407. {
  408. levels = atoi(value_str.c_str());
  409. }
  410. }
  411. else if (!strcmp(argv[arg], "--reversible") || !strcmp(argv[arg], "-rev"))
  412. {
  413. reversible = true;
  414. }
  415. else if (!strcmp(argv[arg], "--logmetrics") || !strcmp(argv[arg], "-log"))
  416. {
  417. // '--logmetrics' needs to be specified with a named test metric argument
  418. // Note: for the moment, only ImageCompressionTester has been tested
  419. std::string test_name;
  420. if ((arg + 1) < argc)
  421. {
  422. test_name = argv[arg+1];
  423. }
  424. if (((arg + 1) >= argc) || (test_name[0] == '-'))
  425. {
  426. // We don't have an argument left in the arg list or the next argument is another option
  427. std::cout << "No --logmetrics argument given, no perf data will be gathered" << std::endl;
  428. }
  429. else
  430. {
  431. LLFastTimer::sMetricLog = TRUE;
  432. LLFastTimer::sLogName = test_name;
  433. arg += 1; // Skip that arg now we know it's a valid test name
  434. if ((arg + 1) == argc) // Break out of the loop if we reach the end of the arg list
  435. break;
  436. }
  437. }
  438. else if (!strcmp(argv[arg], "--analyzeperformance") || !strcmp(argv[arg], "-a"))
  439. {
  440. analyze_performance = true;
  441. }
  442. else if (!strcmp(argv[arg], "--image-stats") || !strcmp(argv[arg], "-s"))
  443. {
  444. image_stats = true;
  445. }
  446. }
  447. // Check arguments consistency. Exit with proper message if inconsistent.
  448. if (input_filenames.size() == 0)
  449. {
  450. std::cout << "No input file, nothing to do -> exit" << std::endl;
  451. return 0;
  452. }
  453. if (analyze_performance && !LLFastTimer::sMetricLog)
  454. {
  455. std::cout << "Cannot create perf report if no perf gathered (i.e. use argument -log <perf> with -a) -> exit" << std::endl;
  456. return 0;
  457. }
  458. // Create the logging thread if required
  459. if (LLFastTimer::sMetricLog)
  460. {
  461. LLFastTimer::sLogLock = new LLMutex(NULL);
  462. fast_timer_log_thread = new LogThread(LLFastTimer::sLogName);
  463. fast_timer_log_thread->start();
  464. }
  465. // Perform action on each input file
  466. std::list<std::string>::iterator in_file = input_filenames.begin();
  467. std::list<std::string>::iterator out_file = output_filenames.begin();
  468. std::list<std::string>::iterator in_end = input_filenames.end();
  469. std::list<std::string>::iterator out_end = output_filenames.end();
  470. for (; in_file != in_end; ++in_file, ++out_file)
  471. {
  472. // Load file
  473. LLPointer<LLImageRaw> raw_image = load_image(*in_file, discard_level, region, image_stats);
  474. if (!raw_image)
  475. {
  476. std::cout << "Error: Image " << *in_file << " could not be loaded" << std::endl;
  477. continue;
  478. }
  479. // Save file
  480. if (out_file != out_end)
  481. {
  482. if (!save_image(*out_file, raw_image, blocks_size, precincts_size, levels, reversible, image_stats))
  483. {
  484. std::cout << "Error: Image " << *out_file << " could not be saved" << std::endl;
  485. }
  486. else
  487. {
  488. std::cout << *in_file << " -> " << *out_file << std::endl;
  489. }
  490. }
  491. }
  492. // Output perf data if requested by user
  493. if (analyze_performance)
  494. {
  495. std::string baseline_name = LLFastTimer::sLogName + "_baseline.slp";
  496. std::string current_name = LLFastTimer::sLogName + ".slp";
  497. std::string report_name = LLFastTimer::sLogName + "_report.csv";
  498. std::cout << "Analyzing performance, check report in : " << report_name << std::endl;
  499. LLMetricPerformanceTesterBasic::doAnalysisMetrics(baseline_name, current_name, report_name);
  500. }
  501. // Stop the perf gathering system if needed
  502. if (LLFastTimer::sMetricLog)
  503. {
  504. LLMetricPerformanceTesterBasic::deleteTester(LLFastTimer::sLogName);
  505. sAllDone = true;
  506. }
  507. // Cleanup and exit
  508. LLImage::cleanupClass();
  509. if (fast_timer_log_thread)
  510. {
  511. fast_timer_log_thread->shutdown();
  512. }
  513. return 0;
  514. }