From 83ec0cd62f70888c90671ea91cd056ecb6095bc1 Mon Sep 17 00:00:00 2001
From: Merov Linden <merov@lindenlab.com>
Date: Mon, 4 Apr 2011 18:37:32 -0700
Subject: [PATCH] STORM-746 : add new arguments for precincts and blocks on
 output, region and level on input, add code for input loading restriction

---
 .../llimage_libtest/llimage_libtest.cpp       | 126 +++++++++++++++++-
 indra/llimage/llimagej2c.cpp                  |   7 +
 indra/llimage/llimagej2c.h                    |   2 +
 indra/llimagej2coj/llimagej2coj.cpp           |   5 +
 indra/llimagej2coj/llimagej2coj.h             |   1 +
 indra/llkdu/llimagej2ckdu.cpp                 |  38 ++++--
 indra/llkdu/llimagej2ckdu.h                   |   3 +-
 7 files changed, 161 insertions(+), 21 deletions(-)

diff --git a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
index 365f5f758cd..10d6ffb685c 100644
--- a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
+++ b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
@@ -53,12 +53,28 @@ static const char USAGE[] = "\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"
+" -r, --region <x0, y0, x1, y1>\n"
+"        Crop region on the input file in pixel.\n"
+"        Only used for j2c images. Default is no region cropping.\n"
+" -d, --discard_level <n>\n"
+"        Discard level max used on input. 0 is high resolution. Max discard level is 5.\n"
+"        This allows the input image to be clamped in resolution when loading.\n"
+"        Only valid for j2c images. Default is no discard.\n"
+" -p, --precincts <n>\n"
+"        Dimension of precincts in pixels. Precincts are assumed square and identical for\n"
+"        all levels. Note that this oprion also uses PLT and tile markers, \n"
+"        as well as RPCL order. Power of 2 must be used.\n"
+"        Only valid for output j2c images. Default is no precincts used.\n"
+" -b, --blocks <n>\n"
+"        Dimension of coding blocks in pixels. Blocks are assumed square. Power of 2 must\n"
+"        be used. Blocks must be smaller than precincts.\n"
+"        Only valid for output j2c images. Default is 64.\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"
+" -a, --analyzeperformance\n"
 "        Create a report comparing <metric>_baseline.slp with current <metric>.slp\n"
-"        Results in <metric>_report.csv"
+"        Results in <metric>_report.csv\n"
 " -s, --image-stats\n"
 "        Output stats for each input and output image.\n"
 "\n";
@@ -110,10 +126,11 @@ void output_image_stats(LLPointer<LLImageFormatted> image, const std::string &fi
 }
 
 // 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<LLImageRaw> load_image(const std::string &src_filename, bool output_stats, bool use_discard_level, int discard_level, bool use_region, int* region)
 {
 	LLPointer<LLImageFormatted> image = create_image(src_filename);
-
+	
+	// This just load the image file stream into a buffer. No decoding done.
 	if (!image->load(src_filename))
 	{
 		return NULL;
@@ -131,6 +148,17 @@ LLPointer<LLImageRaw> load_image(const std::string &src_filename, bool output_st
 	}
 	
 	LLPointer<LLImageRaw> raw_image = new LLImageRaw;
+	
+	// Set the image restriction on load in the case of a j2c image
+	if ((image->getCodec() == IMG_CODEC_J2C) && (use_discard_level || use_region))
+	{
+		int discard = (use_discard_level ? discard_level : -1);
+		int* reg = (use_region ? region : NULL);
+		// That method doesn't exist (and likely, doesn't make sense) for any other image file format
+		// hence the required cryptic cast.
+		((LLImageJ2C*)(image.get()))->initDecode(*raw_image, discard, reg);
+	}
+	
 	if (!image->decode(raw_image, 0.0f))
 	{
 		return NULL;
@@ -280,8 +308,17 @@ int main(int argc, char** argv)
 	// List of input and output files
 	std::list<std::string> input_filenames;
 	std::list<std::string> output_filenames;
+	// Other optional parsed arguments
 	bool analyze_performance = false;
 	bool image_stats = false;
+	bool use_region = false;
+	int region[4];
+	bool use_discard_level = false;
+	int discard_level = 0;
+	bool use_precincts = false;
+	int precincts_size;
+	bool use_blocks = false;
+	int blocks_size;
 
 	// Init whatever is necessary
 	ll_init_apr();
@@ -323,6 +360,81 @@ int main(int argc, char** argv)
 				file_name = argv[arg+1];	// Next argument and loop over
 			}
 		}
+		else if ((!strcmp(argv[arg], "--region") || !strcmp(argv[arg], "-r")) && arg < argc-1)
+		{
+			std::string value_str = argv[arg+1];
+			int index = 0;
+			while (value_str[0] != '-')		// if arg starts with '-', it's the next option
+			{
+				int value = atoi(value_str.c_str());
+				region[index++] = value;
+				arg += 1;					// Definitely skip that arg now we know it's a number
+				if ((arg + 1) == argc)		// Break out of the loop if we reach the end of the arg list
+					break;
+				if (index == 4)				// Break out of the loop if we captured 4 values already
+					break;
+				value_str = argv[arg+1];	// Next argument and loop over
+			}
+			if (index == 4)
+			{
+				use_region = true;
+			}
+			else
+			{
+				std::cout << "--region arguments invalid" << std::endl;
+			}
+		}
+		else if (!strcmp(argv[arg], "--discard_level") || !strcmp(argv[arg], "-d"))
+		{
+			std::string value_str;
+			if ((arg + 1) < argc)
+			{
+				value_str = argv[arg+1];
+			}
+			if (((arg + 1) >= argc) || (value_str[0] == '-'))
+			{
+				std::cout << "No valid --discard_level argument given, discard_level ignored" << std::endl;
+			}
+			else
+			{
+				use_discard_level = true;
+				discard_level = atoi(value_str.c_str());
+			}
+		}
+		else if (!strcmp(argv[arg], "--precincts") || !strcmp(argv[arg], "-p"))
+		{
+			std::string value_str;
+			if ((arg + 1) < argc)
+			{
+				value_str = argv[arg+1];
+			}
+			if (((arg + 1) >= argc) || (value_str[0] == '-'))
+			{
+				std::cout << "No valid --precincts argument given, precincts ignored" << std::endl;
+			}
+			else
+			{
+				use_precincts = true;
+				precincts_size = atoi(value_str.c_str());
+			}
+		}
+		else if (!strcmp(argv[arg], "--blocks") || !strcmp(argv[arg], "-b"))
+		{
+			std::string value_str;
+			if ((arg + 1) < argc)
+			{
+				value_str = argv[arg+1];
+			}
+			if (((arg + 1) >= argc) || (value_str[0] == '-'))
+			{
+				std::cout << "No valid --blocks argument given, blocks ignored" << std::endl;
+			}
+			else
+			{
+				use_blocks = true;
+				blocks_size = atoi(value_str.c_str());
+			}
+		}
 		else if (!strcmp(argv[arg], "--logmetrics") || !strcmp(argv[arg], "-log"))
 		{
 			// '--logmetrics' needs to be specified with a named test metric argument
@@ -346,7 +458,7 @@ int main(int argc, char** argv)
 					break;
 			}
 		}
-		else if (!strcmp(argv[arg], "--analyzeperformance") || !strcmp(argv[arg], "-r"))
+		else if (!strcmp(argv[arg], "--analyzeperformance") || !strcmp(argv[arg], "-a"))
 		{
 			analyze_performance = true;
 		}
@@ -364,7 +476,7 @@ int main(int argc, char** argv)
 	}
 	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;
+		std::cout << "Cannot create perf report if no perf gathered (i.e. use argument -log <perf> with -a) -> exit" << std::endl;
 		return 0;
 	}
 	
@@ -385,7 +497,7 @@ int main(int argc, char** argv)
 	for (; in_file != in_end; ++in_file)
 	{
 		// Load file
-		LLPointer<LLImageRaw> raw_image = load_image(*in_file, image_stats);
+		LLPointer<LLImageRaw> raw_image = load_image(*in_file, image_stats, use_discard_level, discard_level, use_region, region);
 		if (!raw_image)
 		{
 			std::cout << "Error: Image " << *in_file << " could not be loaded" << std::endl;
diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp
index 80fec7f8a04..6b49f3de889 100644
--- a/indra/llimage/llimagej2c.cpp
+++ b/indra/llimage/llimagej2c.cpp
@@ -139,6 +139,10 @@ BOOL LLImageJ2C::updateData()
 	return res;
 }
 
+BOOL LLImageJ2C::initDecode(LLImageRaw &raw_image, int discard_level, int* region)
+{
+	return mImpl->initDecode(*this,raw_image,discard_level,region);
+}
 
 BOOL LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time)
 {
@@ -251,6 +255,9 @@ S32 LLImageJ2C::calcHeaderSizeJ2C()
 //static
 S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate)
 {
+	// Note: this only provides an *estimate* of the size in bytes of an image level
+	// *TODO: find a way to read the true size (when available) and convey the fact
+	// that the result is an estimate in the other cases
 	if (rate <= 0.f) rate = .125f;
 	while (discard_level > 0)
 	{
diff --git a/indra/llimage/llimagej2c.h b/indra/llimage/llimagej2c.h
index dd5bec8b2e2..7af1c139216 100644
--- a/indra/llimage/llimagej2c.h
+++ b/indra/llimage/llimagej2c.h
@@ -56,6 +56,7 @@ class LLImageJ2C : public LLImageFormatted
 	/*virtual*/ void resetLastError();
 	/*virtual*/ void setLastError(const std::string& message, const std::string& filename = std::string());
 	
+	BOOL initDecode(LLImageRaw &raw_image, int discard_level, int* region);
 	
 	// Encode with comment text 
 	BOOL encode(const LLImageRaw *raw_imagep, const char* comment_text, F32 encode_time=0.0);
@@ -117,6 +118,7 @@ class LLImageJ2CImpl
 	virtual BOOL decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) = 0;
 	virtual BOOL encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time=0.0,
 							BOOL reversible=FALSE) = 0;
+	virtual BOOL initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level = -1, int* region = NULL) = 0;
 
 	friend class LLImageJ2C;
 };
diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp
index 13b12c0928c..11c826e41a0 100644
--- a/indra/llimagej2coj/llimagej2coj.cpp
+++ b/indra/llimagej2coj/llimagej2coj.cpp
@@ -107,6 +107,11 @@ LLImageJ2COJ::~LLImageJ2COJ()
 {
 }
 
+BOOL LLImageJ2COJ::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level, int* region)
+{
+	// No specific implementaion for this method in the OpenJpeg case
+	return FALSE;
+}
 
 BOOL LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count)
 {
diff --git a/indra/llimagej2coj/llimagej2coj.h b/indra/llimagej2coj/llimagej2coj.h
index 9476665ccbb..d5f2f7a2d1d 100644
--- a/indra/llimagej2coj/llimagej2coj.h
+++ b/indra/llimagej2coj/llimagej2coj.h
@@ -39,6 +39,7 @@ class LLImageJ2COJ : public LLImageJ2CImpl
 	/*virtual*/ BOOL decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count);
 	/*virtual*/ BOOL encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time=0.0,
 								BOOL reversible = FALSE);
+	/*virtual*/ BOOL initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level = -1, int* region = NULL);
 };
 
 #endif
diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp
index 61b16c80e67..e51cfee6eb7 100644
--- a/indra/llkdu/llimagej2ckdu.cpp
+++ b/indra/llkdu/llimagej2ckdu.cpp
@@ -106,7 +106,11 @@ const char* fallbackEngineInfoLLImageJ2CImpl()
 class LLKDUDecodeState
 {
 public:
+	LLKDUDecodeState(kdu_tile tile, kdu_byte *buf, S32 row_gap);
+	~LLKDUDecodeState();
+	BOOL processTileDecode(F32 decode_time, BOOL limit_time = TRUE);
 
+private:
 	S32 mNumComponents;
 	BOOL mUseYCC;
 	kdu_dims mDims;
@@ -116,20 +120,10 @@ class LLKDUDecodeState
 	kdu_pull_ifc mEngines[4];
 	bool mReversible[4]; // Some components may be reversible and others not
 	int mBitDepths[4];   // Original bit-depth may be quite different from 8
-
+	
 	kdu_tile mTile;
 	kdu_byte *mBuf;
 	S32 mRowGap;
-
-	LLKDUDecodeState(kdu_tile tile, kdu_byte *buf, S32 row_gap);
-	~LLKDUDecodeState();
-	BOOL processTileDecode(F32 decode_time, BOOL limit_time = TRUE);
-
-public:
-	int *AssignLayerBytes(siz_params *siz, int &num_specs);
-
-	void setupCodeStream(BOOL keep_codestream, LLImageJ2CKDU::ECodeStreamMode mode);
-	BOOL initDecode(LLImageRaw &raw_image, F32 decode_time, LLImageJ2CKDU::ECodeStreamMode mode, S32 first_channel, S32 max_channel_count );
 };
 
 void ll_kdu_error( void )
@@ -327,7 +321,12 @@ void LLImageJ2CKDU::cleanupCodeStream()
 	mTileIndicesp = NULL;
 }
 
-BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count )
+BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level, int* region)
+{
+	return initDecode(base,raw_image,0.0f,MODE_FAST,0,4,discard_level,region);
+}
+
+BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count, int discard_level, int* region)
 {
 	base.resetLastError();
 
@@ -340,7 +339,20 @@ BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
 
 		mRawImagep = &raw_image;
 		mCodeStreamp->change_appearance(false, true, false);
-		mCodeStreamp->apply_input_restrictions(first_channel,max_channel_count,base.getRawDiscardLevel(),0,NULL);
+
+		// Apply loading discard level and cropping if required
+		kdu_dims* region_kdu = NULL;
+		if (region != NULL)
+		{
+			region_kdu = new kdu_dims;
+			region_kdu->pos.x  = region[0];
+			region_kdu->pos.y  = region[1];
+			region_kdu->size.x = region[2] - region[0];
+			region_kdu->size.y = region[3] - region[1];
+		}
+		int discard = (discard_level != -1 ? discard_level : base.getRawDiscardLevel());
+		
+		mCodeStreamp->apply_input_restrictions( first_channel, max_channel_count, discard, 0, region_kdu);
 
 		kdu_dims dims; mCodeStreamp->get_dims(0,dims);
 		S32 channels = base.getComponents() - first_channel;
diff --git a/indra/llkdu/llimagej2ckdu.h b/indra/llkdu/llimagej2ckdu.h
index 5628f69eeb6..8231004f2c4 100644
--- a/indra/llkdu/llimagej2ckdu.h
+++ b/indra/llkdu/llimagej2ckdu.h
@@ -58,11 +58,12 @@ class LLImageJ2CKDU : public LLImageJ2CImpl
 	/*virtual*/ BOOL decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count);
 	/*virtual*/ BOOL encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time=0.0,
 								BOOL reversible=FALSE);
+	/*virtual*/ BOOL initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level = -1, int* region = NULL);
 
 private:
+	BOOL initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count, int discard_level = -1, int* region = NULL);
 	void setupCodeStream(LLImageJ2C &base, BOOL keep_codestream, ECodeStreamMode mode);
 	void cleanupCodeStream();
-	BOOL initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count );
 
 	// Encode variable
 	LLKDUMemSource *mInputp;
-- 
GitLab