diff options
| -rw-r--r-- | autobuild.xml | 14 | ||||
| -rw-r--r-- | indra/cmake/Copy3rdPartyLibs.cmake | 4 | ||||
| -rw-r--r-- | indra/cmake/FindOpenJPEG.cmake | 5 | ||||
| -rw-r--r-- | indra/cmake/OpenJPEG.cmake | 14 | ||||
| -rw-r--r-- | indra/integration_tests/llui_libtest/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | indra/llimagej2coj/llimagej2coj.cpp | 1101 | ||||
| -rw-r--r-- | indra/newview/CMakeLists.txt | 6 | ||||
| -rwxr-xr-x | indra/newview/viewer_manifest.py | 4 | 
8 files changed, 744 insertions, 408 deletions
| diff --git a/autobuild.xml b/autobuild.xml index 5a5ffa2898..801da4e773 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -2195,9 +2195,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>              <key>archive</key>              <map>                <key>hash</key> -              <string>5abf2d9c0b250821c59cc60cd94fd8af</string> +              <string>89e6436b256c98818fa736a460a04016</string>                <key>url</key> -              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54840/510064/openjpeg-1.5.1.538970-darwin64-538970.tar.bz2</string> +              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105184/920580/openjpeg-2.5.0.575325-darwin64-575325.tar.bz2</string>              </map>              <key>name</key>              <string>darwin64</string> @@ -2219,9 +2219,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>              <key>archive</key>              <map>                <key>hash</key> -              <string>222a406ecb4071a9cc9635353afa337e</string> +              <string>97b15b312c2dec38981ec63bd77d0491</string>                <key>url</key> -              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54977/511775/openjpeg-1.5.1.538970-windows-538970.tar.bz2</string> +              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105185/920592/openjpeg-2.5.0.575325-windows-575325.tar.bz2</string>              </map>              <key>name</key>              <string>windows</string> @@ -2231,16 +2231,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>              <key>archive</key>              <map>                <key>hash</key> -              <string>5b5c80807fa8161f3480be3d89fe9516</string> +              <string>cdb55c8b074ce4f166ebc597f13f62a1</string>                <key>url</key> -              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54974/511767/openjpeg-1.5.1.538970-windows64-538970.tar.bz2</string> +              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105186/920591/openjpeg-2.5.0.575325-windows64-575325.tar.bz2</string>              </map>              <key>name</key>              <string>windows64</string>            </map>          </map>          <key>version</key> -        <string>1.5.1.538970</string> +        <string>2.5.0.575325</string>        </map>        <key>openssl</key>        <map> diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index ff705101de..04b4202ca3 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -52,7 +52,7 @@ if(WINDOWS)      set(release_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}")      set(release_files -        openjpeg.dll +        openjp2.dll          libapr-1.dll          libaprutil-1.dll          libapriconv-1.dll @@ -220,7 +220,7 @@ elseif(LINUX)          libgobject-2.0.so          libhunspell-1.3.so.0.0.0          libopenal.so -        libopenjpeg.so +        libopenjp2.so          libuuid.so.16          libuuid.so.16.0.22          libfontconfig.so.1.8.0 diff --git a/indra/cmake/FindOpenJPEG.cmake b/indra/cmake/FindOpenJPEG.cmake index 949384eec4..afe319fb06 100644 --- a/indra/cmake/FindOpenJPEG.cmake +++ b/indra/cmake/FindOpenJPEG.cmake @@ -9,14 +9,15 @@  # also defined, but not for general use are  #  OPENJPEG_LIBRARY, where to find the OpenJPEG library. -FIND_PATH(OPENJPEG_INCLUDE_DIR openjpeg.h +FIND_PATH(OPENJPEG_INCLUDE_DIR openjp2.h  /usr/local/include/openjpeg  /usr/local/include  /usr/include/openjpeg  /usr/include +include/openjpeg  ) -SET(OPENJPEG_NAMES ${OPENJPEG_NAMES} openjpeg) +SET(OPENJPEG_NAMES ${OPENJPEG_NAMES} openjp2)  FIND_LIBRARY(OPENJPEG_LIBRARY    NAMES ${OPENJPEG_NAMES}    PATHS /usr/lib /usr/local/lib diff --git a/indra/cmake/OpenJPEG.cmake b/indra/cmake/OpenJPEG.cmake index bf0bde2ba7..a078c97cb8 100644 --- a/indra/cmake/OpenJPEG.cmake +++ b/indra/cmake/OpenJPEG.cmake @@ -8,15 +8,7 @@ if (USESYSTEMLIBS)    include(FindOpenJPEG)  else (USESYSTEMLIBS)    use_prebuilt_binary(openjpeg) -   -  if(WINDOWS) -    # Windows has differently named release and debug openjpeg(d) libs. -    set(OPENJPEG_LIBRARIES  -        debug openjpegd -        optimized openjpeg) -  else(WINDOWS) -    set(OPENJPEG_LIBRARIES openjpeg) -  endif(WINDOWS) -   -    set(OPENJPEG_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/openjpeg) + +  set(OPENJPEG_LIBRARIES openjp2) +  set(OPENJPEG_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/openjpeg)  endif (USESYSTEMLIBS) diff --git a/indra/integration_tests/llui_libtest/CMakeLists.txt b/indra/integration_tests/llui_libtest/CMakeLists.txt index d7706e73b2..3957ede77f 100644 --- a/indra/integration_tests/llui_libtest/CMakeLists.txt +++ b/indra/integration_tests/llui_libtest/CMakeLists.txt @@ -99,14 +99,14 @@ if (WINDOWS)      # Copy over OpenJPEG.dll      # *NOTE: On Windows with VS2005, only the first comment prints      set(OPENJPEG_RELEASE -        "${ARCH_PREBUILT_DIRS_RELEASE}/openjpeg.dll") +        "${ARCH_PREBUILT_DIRS_RELEASE}/openjp2.dll")      add_custom_command( TARGET llui_libtest POST_BUILD          COMMAND ${CMAKE_COMMAND} -E copy_if_different               ${OPENJPEG_RELEASE} ${CMAKE_CURRENT_BINARY_DIR}          COMMENT "Copying OpenJPEG DLLs to binary directory"          )      set(OPENJPEG_DEBUG -        "${ARCH_PREBUILT_DIRS_DEBUG}/openjpegd.dll") +        "${ARCH_PREBUILT_DIRS_DEBUG}/openjp2.dll")      add_custom_command( TARGET llui_libtest POST_BUILD          COMMAND ${CMAKE_COMMAND} -E copy_if_different               ${OPENJPEG_DEBUG} ${CMAKE_CURRENT_BINARY_DIR} diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp index 925da5674b..944629cf4c 100644 --- a/indra/llimagej2coj/llimagej2coj.cpp +++ b/indra/llimagej2coj/llimagej2coj.cpp @@ -29,40 +29,41 @@  // this is defined so that we get static linking.  #include "openjpeg.h" +#include "event.h" +#include "cio.h" -#include "lltimer.h" -//#include "llmemory.h" +#define MAX_ENCODED_DISCARD_LEVELS 5  // Factory function: see declaration in llimagej2c.cpp  LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl()  { -	return new LLImageJ2COJ(); +    return new LLImageJ2COJ();  }  std::string LLImageJ2COJ::getEngineInfo() const  {  #ifdef OPENJPEG_VERSION -	return std::string("OpenJPEG: " OPENJPEG_VERSION ", Runtime: ") -		+ opj_version(); +    return std::string("OpenJPEG: " OPENJPEG_VERSION ", Runtime: ") +        + opj_version();  #else -	return std::string("OpenJPEG runtime: ") + opj_version(); +    return std::string("OpenJPEG runtime: ") + opj_version();  #endif  }  // Return string from message, eliminating final \n if present  static std::string chomp(const char* msg)  { -	// stomp trailing \n -	std::string message = msg; -	if (!message.empty()) -	{ -		size_t last = message.size() - 1; -		if (message[last] == '\n') -		{ -			message.resize( last ); -		} -	} -	return message; +    // stomp trailing \n +    std::string message = msg; +    if (!message.empty()) +    { +        size_t last = message.size() - 1; +        if (message[last] == '\n') +        { +            message.resize(last); +        } +} +    return message;  }  /** @@ -70,419 +71,761 @@ sample error callback expecting a LLFILE* client object  */  void error_callback(const char* msg, void*)  { -	LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL; +    LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL;  }  /**  sample warning callback expecting a LLFILE* client object  */  void warning_callback(const char* msg, void*)  { -	LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL; +    LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL;  }  /**  sample debug callback expecting no client object  */  void info_callback(const char* msg, void*)  { -	LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL; +    LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL;  }  // Divide a by 2 to the power of b and round upwards  int ceildivpow2(int a, int b)  { -	return (a + (1 << b) - 1) >> b; +    return (a + (1 << b) - 1) >> b;  } - -LLImageJ2COJ::LLImageJ2COJ() -	: LLImageJ2CImpl() +class JPEG2KBase  { -} +public: +    JPEG2KBase() {} +    U8*        buffer = nullptr; +    OPJ_SIZE_T size = 0; +    OPJ_OFF_T  offset = 0; +}; -LLImageJ2COJ::~LLImageJ2COJ() +#define WANT_VERBOSE_OPJ_SPAM LL_DEBUG + +static void opj_info(const char* msg, void* user_data)  { +    llassert(user_data); +#if WANT_VERBOSE_OPJ_SPAM +    LL_INFOS("OpenJPEG") << msg << LL_ENDL; +#endif  } -bool LLImageJ2COJ::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level, int* region) +static void opj_warn(const char* msg, void* user_data)  { -	// No specific implementation for this method in the OpenJpeg case -	return false; +    llassert(user_data); +#if WANT_VERBOSE_OPJ_SPAM +    LL_WARNS("OpenJPEG") << msg << LL_ENDL; +#endif  } -bool LLImageJ2COJ::initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size, int precincts_size, int levels) +static void opj_error(const char* msg, void* user_data)  { -	// No specific implementation for this method in the OpenJpeg case -	return false; +    llassert(user_data); +#if WANT_VERBOSE_OPJ_SPAM +    LL_WARNS("OpenJPEG") << msg << LL_ENDL; +#endif  } -bool LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) +static OPJ_SIZE_T opj_read(void * buffer, OPJ_SIZE_T bytes, void* user_data)  { -	// -	// FIXME: Get the comment field out of the texture -	// - -	LLTimer decode_timer; - -	opj_dparameters_t parameters;	/* decompression parameters */ -	opj_event_mgr_t event_mgr;		/* event manager */ -	opj_image_t *image = NULL; - -	opj_dinfo_t* dinfo = NULL;	/* handle to a decompressor */ -	opj_cio_t *cio = NULL; - - -	/* configure the event callbacks (not required) */ -	memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); -	event_mgr.error_handler = error_callback; -	event_mgr.warning_handler = warning_callback; -	event_mgr.info_handler = info_callback; - -	/* set decoding parameters to default values */ -	opj_set_default_decoder_parameters(¶meters); - -	parameters.cp_reduce = base.getRawDiscardLevel(); - -	/* decode the code-stream */ -	/* ---------------------- */ - -	/* JPEG-2000 codestream */ - -	/* get a decoder handle */ -	dinfo = opj_create_decompress(CODEC_J2K); - -	/* catch events using our callbacks and give a local context */ -	opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, stderr);			 - -	/* setup the decoder decoding parameters using user parameters */ -	opj_setup_decoder(dinfo, ¶meters); - -	/* open a byte stream */ -	cio = opj_cio_open((opj_common_ptr)dinfo, base.getData(), base.getDataSize()); - -	/* decode the stream and fill the image structure */ -	image = opj_decode(dinfo, cio); - -	/* close the byte stream */ -	opj_cio_close(cio); - -	/* free remaining structures */ -	if(dinfo) -	{ -		opj_destroy_decompress(dinfo); -	} - -	// The image decode failed if the return was NULL or the component -	// count was zero.  The latter is just a sanity check before we -	// dereference the array. -	if(!image || !image->numcomps) -	{ -		LL_DEBUGS("Texture") << "ERROR -> decodeImpl: failed to decode image!" << LL_ENDL; -		if (image) -		{ -			opj_image_destroy(image); -		} - -		return true; // done -	} - -	// sometimes we get bad data out of the cache - check to see if the decode succeeded -	for (S32 i = 0; i < image->numcomps; i++) -	{ -		if (image->comps[i].factor != base.getRawDiscardLevel()) -		{ -			// if we didn't get the discard level we're expecting, fail -			opj_image_destroy(image); -			base.mDecoding = false; -			return true; -		} -	} -	 -	if(image->numcomps <= first_channel) -	{ -		LL_WARNS() << "trying to decode more channels than are present in image: numcomps: " << image->numcomps << " first_channel: " << first_channel << LL_ENDL; -		if (image) -		{ -			opj_image_destroy(image); -		} -			 -		return true; -	} - -	// Copy image data into our raw image format (instead of the separate channel format - -	S32 img_components = image->numcomps; -	S32 channels = img_components - first_channel; -	if( channels > max_channel_count ) -		channels = max_channel_count; - -	// Component buffers are allocated in an image width by height buffer. -	// The image placed in that buffer is ceil(width/2^factor) by -	// ceil(height/2^factor) and if the factor isn't zero it will be at the -	// top left of the buffer with black filled in the rest of the pixels. -	// It is integer math so the formula is written in ceildivpo2. -	// (Assuming all the components have the same width, height and -	// factor.) -	S32 comp_width = image->comps[0].w; -	S32 f=image->comps[0].factor; -	S32 width = ceildivpow2(image->x1 - image->x0, f); -	S32 height = ceildivpow2(image->y1 - image->y0, f); -	raw_image.resize(width, height, channels); -	U8 *rawp = raw_image.getData(); - -	// first_channel is what channel to start copying from -	// dest is what channel to copy to.  first_channel comes from the -	// argument, dest always starts writing at channel zero. -	for (S32 comp = first_channel, dest=0; comp < first_channel + channels; -		comp++, dest++) -	{ -		if (image->comps[comp].data) -		{ -			S32 offset = dest; -			for (S32 y = (height - 1); y >= 0; y--) -			{ -				for (S32 x = 0; x < width; x++) -				{ -					rawp[offset] = image->comps[comp].data[y*comp_width + x]; -					offset += channels; -				} -			} -		} -		else // Some rare OpenJPEG versions have this bug. -		{ -			LL_DEBUGS("Texture") << "ERROR -> decodeImpl: failed to decode image! (NULL comp data - OpenJPEG bug)" << LL_ENDL; -			opj_image_destroy(image); - -			return true; // done -		} -	} - -	/* free image data structure */ -	opj_image_destroy(image); - -	return true; // done +    llassert(user_data); +    JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data); +    OPJ_SIZE_T remainder = (jpeg_codec->size - jpeg_codec->offset); +    if (remainder <= 0) +    { +        jpeg_codec->offset = jpeg_codec->size; +        // Indicate end of stream (hacky?) +        return (OPJ_OFF_T)-1; +    } +    OPJ_SIZE_T to_read = llclamp(U32(bytes), U32(0), U32(remainder)); +    memcpy(buffer, jpeg_codec->buffer + jpeg_codec->offset, to_read); +    jpeg_codec->offset += to_read; +    return to_read;  } - -bool LLImageJ2COJ::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time, bool reversible) +static OPJ_SIZE_T opj_write(void * buffer, OPJ_SIZE_T bytes, void* user_data)  { -	const S32 MAX_COMPS = 5; -	opj_cparameters_t parameters;	/* compression parameters */ -	opj_event_mgr_t event_mgr;		/* event manager */ - - -	/*  -	configure the event callbacks (not required) -	setting of each callback is optional  -	*/ -	memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); -	event_mgr.error_handler = error_callback; -	event_mgr.warning_handler = warning_callback; -	event_mgr.info_handler = info_callback; - -	/* set encoding parameters to default values */ -	opj_set_default_encoder_parameters(¶meters); -	parameters.cod_format = 0; -	parameters.cp_disto_alloc = 1; - -	if (reversible) -	{ -		parameters.tcp_numlayers = 1; -		parameters.tcp_rates[0] = 0.0f; -	} -	else -	{ -		parameters.tcp_numlayers = 5; -                parameters.tcp_rates[0] = 1920.0f; -                parameters.tcp_rates[1] = 480.0f; -                parameters.tcp_rates[2] = 120.0f; -                parameters.tcp_rates[3] = 30.0f; -		parameters.tcp_rates[4] = 10.0f; -		parameters.irreversible = 1; -		if (raw_image.getComponents() >= 3) -		{ -			parameters.tcp_mct = 1; -		} -	} - -	if (!comment_text) -	{ -		parameters.cp_comment = (char *) ""; -	} -	else -	{ -		// Awful hacky cast, too lazy to copy right now. -		parameters.cp_comment = (char *) comment_text; -	} - -	// -	// Fill in the source image from our raw image -	// -	OPJ_COLOR_SPACE color_space = CLRSPC_SRGB; -	opj_image_cmptparm_t cmptparm[MAX_COMPS]; -	opj_image_t * image = NULL; -	S32 numcomps = raw_image.getComponents(); -	S32 width = raw_image.getWidth(); -	S32 height = raw_image.getHeight(); - -	memset(&cmptparm[0], 0, MAX_COMPS * sizeof(opj_image_cmptparm_t)); -	for(S32 c = 0; c < numcomps; c++) { -		cmptparm[c].prec = 8; -		cmptparm[c].bpp = 8; -		cmptparm[c].sgnd = 0; -		cmptparm[c].dx = parameters.subsampling_dx; -		cmptparm[c].dy = parameters.subsampling_dy; -		cmptparm[c].w = width; -		cmptparm[c].h = height; -	} - -	/* create the image */ -	image = opj_image_create(numcomps, &cmptparm[0], color_space); - -	image->x1 = width; -	image->y1 = height; - -	S32 i = 0; -	const U8 *src_datap = raw_image.getData(); -	for (S32 y = height - 1; y >= 0; y--) -	{ -		for (S32 x = 0; x < width; x++) -		{ -			const U8 *pixel = src_datap + (y*width + x) * numcomps; -			for (S32 c = 0; c < numcomps; c++) -			{ -				image->comps[c].data[i] = *pixel; -				pixel++; -			} -			i++; -		} -	} - - - -	/* encode the destination image */ -	/* ---------------------------- */ - -	int codestream_length; -	opj_cio_t *cio = NULL; - -	/* get a J2K compressor handle */ -	opj_cinfo_t* cinfo = opj_create_compress(CODEC_J2K); - -	/* catch events using our callbacks and give a local context */ -	opj_set_event_mgr((opj_common_ptr)cinfo, &event_mgr, stderr);			 - -	/* setup the encoder parameters using the current image and using user parameters */ -	opj_setup_encoder(cinfo, ¶meters, image); - -	/* open a byte stream for writing */ -	/* allocate memory for all tiles */ -	cio = opj_cio_open((opj_common_ptr)cinfo, NULL, 0); - -	/* encode the image */ -	bool bSuccess = opj_encode(cinfo, cio, image, NULL); -	if (!bSuccess) -	{ -		opj_cio_close(cio); -		LL_DEBUGS("Texture") << "Failed to encode image." << LL_ENDL; -		return false; -	} -	codestream_length = cio_tell(cio); - -	base.copyData(cio->buffer, codestream_length); -	base.updateData(); // set width, height - -	/* close and free the byte stream */ -	opj_cio_close(cio); - -	/* free remaining compression structures */ -	opj_destroy_compress(cinfo); - - -	/* free user parameters structure */ -	if(parameters.cp_matrice) free(parameters.cp_matrice); - -	/* free image data */ -	opj_image_destroy(image); -	return true; +    llassert(user_data); +    JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data); +    OPJ_SIZE_T remainder = jpeg_codec->size - jpeg_codec->offset; +    if (remainder < bytes) +    { +        OPJ_SIZE_T new_size = jpeg_codec->size + (bytes - remainder); +        U8* new_buffer = (U8*)ll_aligned_malloc_16(new_size); +        memcpy(new_buffer, jpeg_codec->buffer, jpeg_codec->offset); +        U8* old_buffer = jpeg_codec->buffer; +        jpeg_codec->buffer = new_buffer; +        ll_aligned_free_16(old_buffer); +        jpeg_codec->size = new_size; +    } +    memcpy(jpeg_codec->buffer + jpeg_codec->offset, buffer, bytes); +    jpeg_codec->offset += bytes; +    return bytes;  } -bool LLImageJ2COJ::getMetadata(LLImageJ2C &base) +static OPJ_OFF_T opj_skip(OPJ_OFF_T bytes, void* user_data)  { -	// -	// FIXME: We get metadata by decoding the ENTIRE image. -	// - -	// Update the raw discard level -	base.updateRawDiscardLevel(); - -	opj_dparameters_t parameters;	/* decompression parameters */ -	opj_event_mgr_t event_mgr;		/* event manager */ -	opj_image_t *image = NULL; - -	opj_dinfo_t* dinfo = NULL;	/* handle to a decompressor */ -	opj_cio_t *cio = NULL; - - -	/* configure the event callbacks (not required) */ -	memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); -	event_mgr.error_handler = error_callback; -	event_mgr.warning_handler = warning_callback; -	event_mgr.info_handler = info_callback; - -	/* set decoding parameters to default values */ -	opj_set_default_decoder_parameters(¶meters); +    JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data); +    jpeg_codec->offset += bytes; +    if (jpeg_codec->offset > jpeg_codec->size) +    { +        jpeg_codec->offset = jpeg_codec->size; +        // Indicate end of stream +        return (OPJ_OFF_T)-1; +    } +    return bytes; +} -	// Only decode what's required to get the size data. -	parameters.cp_limit_decoding=LIMIT_TO_MAIN_HEADER; +static OPJ_BOOL opj_seek(OPJ_OFF_T bytes, void * user_data) +{ +    JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data); +    jpeg_codec->offset += bytes; +    jpeg_codec->offset = llclamp(U32(jpeg_codec->offset), U32(0), U32(jpeg_codec->size)); +    return OPJ_TRUE; +} -	//parameters.cp_reduce = mRawDiscardLevel; +static void opj_free_user_data(void * user_data) +{ +    JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data); +    // Don't free, data is managed externally +    jpeg_codec->buffer = nullptr; +    jpeg_codec->size = 0; +    jpeg_codec->offset = 0; +} -	/* decode the code-stream */ -	/* ---------------------- */ +static void opj_free_user_data_write(void * user_data) +{ +    JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data); +    // Free, data was allocated here +    ll_aligned_free_16(jpeg_codec->buffer); +    jpeg_codec->buffer = nullptr; +    jpeg_codec->size = 0; +    jpeg_codec->offset = 0; +} -	/* JPEG-2000 codestream */ +class JPEG2KDecode : public JPEG2KBase +{ +public: + +    JPEG2KDecode(S8 discardLevel) +    { +        memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); +        memset(¶meters, 0, sizeof(opj_dparameters_t)); +        event_mgr.error_handler = error_callback; +        event_mgr.warning_handler = warning_callback; +        event_mgr.info_handler = info_callback; +        opj_set_default_decoder_parameters(¶meters); +        parameters.cp_reduce = discardLevel; +    } + +    ~JPEG2KDecode() +    { +        if (decoder) +        { +            opj_destroy_codec(decoder); +        } +        decoder = nullptr; + +        if (image) +        { +            opj_image_destroy(image); +        } +        image = nullptr; + +        if (stream) +        { +            opj_stream_destroy(stream); +        } +        stream = nullptr; + +        if (codestream_info) +        { +            opj_destroy_cstr_info(&codestream_info); +        } +        codestream_info = nullptr; +    } + +    bool readHeader( +        U8* data, +        U32 dataSize, +        S32& widthOut, +        S32& heightOut, +        S32& components, +        S32& discard_level) +    { +        parameters.flags |= OPJ_DPARAMETERS_DUMP_FLAG; + +        decoder = opj_create_decompress(OPJ_CODEC_J2K); + +        if (!opj_setup_decoder(decoder, ¶meters)) +        { +            return false; +        } + +        if (stream) +        { +            opj_stream_destroy(stream); +        } + +        stream = opj_stream_create(dataSize, true); +        if (!stream) +        { +            return false; +        } + +        opj_stream_set_user_data(stream, this, opj_free_user_data); +        opj_stream_set_user_data_length(stream, dataSize); +        opj_stream_set_read_function(stream, opj_read); +        opj_stream_set_write_function(stream, opj_write); +        opj_stream_set_skip_function(stream, opj_skip); +        opj_stream_set_seek_function(stream, opj_seek); + +        buffer = data; +        size = dataSize; +        offset = 0; + +        // enable decoding partially loaded images +        opj_decoder_set_strict_mode(decoder, OPJ_FALSE); + +        /* Read the main header of the codestream and if necessary the JP2 boxes*/ +        if (!opj_read_header((opj_stream_t*)stream, decoder, &image)) +        { +            return false; +        } + +        codestream_info = opj_get_cstr_info(decoder); + +        if (!codestream_info) +        { +            return false; +        } + +        U32 tileDimX = codestream_info->tdx; +        U32 tileDimY = codestream_info->tdy; +        U32 tilesW = codestream_info->tw; +        U32 tilesH = codestream_info->th; + +        widthOut = S32(tilesW * tileDimX); +        heightOut = S32(tilesH * tileDimY); +        components = codestream_info->nbcomps; + +        discard_level = 0; +        while (tilesW > 1 && tilesH > 1 && discard_level < MAX_DISCARD_LEVEL) +        { +            discard_level++; +            tilesW >>= 1; +            tilesH >>= 1; +        } + +        return true; +    } + +    bool decode(U8* data, U32 dataSize, U32* channels, U8 discard_level) +    { +        parameters.flags &= ~OPJ_DPARAMETERS_DUMP_FLAG; + +        decoder = opj_create_decompress(OPJ_CODEC_J2K); +        opj_setup_decoder(decoder, ¶meters); + +        opj_set_info_handler(decoder, opj_info, this); +        opj_set_warning_handler(decoder, opj_warn, this); +        opj_set_error_handler(decoder, opj_error, this); + +        if (stream) +        { +            opj_stream_destroy(stream); +        } + +        stream = opj_stream_create(dataSize, true); +        if (!stream) +        { +            return false; +        } + +        opj_stream_set_user_data(stream, this, opj_free_user_data); +        opj_stream_set_user_data_length(stream, dataSize); +        opj_stream_set_read_function(stream, opj_read); +        opj_stream_set_write_function(stream, opj_write); +        opj_stream_set_skip_function(stream, opj_skip); +        opj_stream_set_seek_function(stream, opj_seek); + +        buffer = data; +        size = dataSize; +        offset = 0; + +        if (image) +        { +            opj_image_destroy(image); +            image = nullptr; +        } + +        // needs to happen before opj_read_header and opj_decode... +        opj_set_decoded_resolution_factor(decoder, discard_level); + +        // enable decoding partially loaded images +        opj_decoder_set_strict_mode(decoder, OPJ_FALSE); + +        if (!opj_read_header(stream, decoder, &image)) +        { +            return false; +        } + +        // needs to happen before decode which may fail +        if (channels) +        { +            *channels = image->numcomps; +        } + +        OPJ_BOOL decoded = opj_decode(decoder, stream, image); + +        // count was zero.  The latter is just a sanity check before we +        // dereference the array. +        if (!decoded || !image || !image->numcomps) +        { +            opj_end_decompress(decoder, stream); +            return false; +        } + +        opj_end_decompress(decoder, stream); + +        return true; +    } + +    opj_image_t* getImage() { return image; } + +private: +    opj_dparameters_t         parameters; +    opj_event_mgr_t           event_mgr; +    opj_image_t*              image = nullptr; +    opj_codec_t*              decoder = nullptr; +    opj_stream_t*             stream = nullptr; +    opj_codestream_info_v2_t* codestream_info = nullptr; +}; + +class JPEG2KEncode : public JPEG2KBase +{ +public: +    const OPJ_UINT32 TILE_SIZE = 64 * 64 * 3; + +    JPEG2KEncode(const char* comment_text_in, bool reversible) +    { +        memset(¶meters, 0, sizeof(opj_cparameters_t)); +        memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); +        event_mgr.error_handler = error_callback; +        event_mgr.warning_handler = warning_callback; +        event_mgr.info_handler = info_callback; + +        opj_set_default_encoder_parameters(¶meters); +        parameters.cod_format = 0; +        parameters.cp_disto_alloc = 1; +        parameters.max_cs_size = (1 << 15); + +        if (reversible) +        { +            parameters.tcp_numlayers = 1; +            parameters.tcp_rates[0] = 1.0f; +        } +        else +        { +            parameters.tcp_numlayers = 5; +            parameters.tcp_rates[0] = 1920.0f; +            parameters.tcp_rates[1] = 960.0f; +            parameters.tcp_rates[2] = 480.0f; +            parameters.tcp_rates[3] = 120.0f; +            parameters.tcp_rates[4] = 30.0f; +            parameters.irreversible = 1; +            parameters.tcp_mct = 1; +        } + +        if (comment_text) +        { +            free(comment_text); +        } +        comment_text = comment_text_in ? strdup(comment_text_in) : nullptr; + +        parameters.cp_comment = comment_text ? comment_text : (char*)"no comment"; +        llassert(parameters.cp_comment); +    } + +    ~JPEG2KEncode() +    { +        if (encoder) +        { +            opj_destroy_codec(encoder); +        } +        encoder = nullptr; + +        if (image) +        { +            opj_image_destroy(image); +        } +        image = nullptr; + +        if (stream) +        { +            opj_stream_destroy(stream); +        } +        stream = nullptr; + +        if (comment_text) +        { +            free(comment_text); +        } +        comment_text = nullptr; +    } + +    bool encode(const LLImageRaw& rawImageIn, LLImageJ2C &compressedImageOut) +    { +        setImage(rawImageIn); + +        encoder = opj_create_compress(OPJ_CODEC_J2K); + +        parameters.tcp_mct = (image->numcomps >= 3) ? 1 : 0; +        parameters.cod_format = OPJ_CODEC_J2K; +        parameters.numresolution = MAX_ENCODED_DISCARD_LEVELS; +        parameters.prog_order = OPJ_RLCP; +        parameters.cp_disto_alloc = 1; + +        opj_setup_encoder(encoder, ¶meters, image); + +        opj_set_info_handler(encoder, opj_info, this); +        opj_set_warning_handler(encoder, opj_warn, this); +        opj_set_error_handler(encoder, opj_error, this); + +        U32 tile_count = (rawImageIn.getWidth() >> 6) * (rawImageIn.getHeight() >> 6); +        U32 data_size_guess = tile_count * TILE_SIZE; + +        // will be freed in opj_free_user_data_write +        buffer = (U8*)ll_aligned_malloc_16(data_size_guess); +        size = data_size_guess; +        offset = 0; + +#if LL_DEBUG +        memset(buffer, 0, data_size_guess); +#endif -	/* get a decoder handle */ -	dinfo = opj_create_decompress(CODEC_J2K); +        if (stream) +        { +            opj_stream_destroy(stream); +        } + +        stream = opj_stream_create(data_size_guess, false); +        if (!stream) +        { +            return false; +        } + +        opj_stream_set_user_data(stream, this, opj_free_user_data_write); +        opj_stream_set_user_data_length(stream, data_size_guess); +        opj_stream_set_read_function(stream, opj_read); +        opj_stream_set_write_function(stream, opj_write); +        opj_stream_set_skip_function(stream, opj_skip); +        opj_stream_set_seek_function(stream, opj_seek); + +        OPJ_BOOL started = opj_start_compress(encoder, image, stream); + +        if (!started) +        { +            return false; +        } + +        if (!opj_encode(encoder, stream)) +        { +            return false; +        } + +        OPJ_BOOL encoded = opj_end_compress(encoder, stream); + +        // if we successfully encoded, then stream out the compressed data... +        if (encoded) +        { +            // "append" (set) the data we "streamed" (memcopied) for writing to the formatted image +            // with side-effect of setting the actually encoded size  to same +            compressedImageOut.allocateData(offset); +            memcpy(compressedImageOut.getData(), buffer, offset); +            compressedImageOut.updateData(); // update width, height etc from header +        } +        return encoded; +    } + +    void setImage(const LLImageRaw& raw) +    { +        opj_image_cmptparm_t cmptparm[MAX_ENCODED_DISCARD_LEVELS]; +        memset(&cmptparm[0], 0, MAX_ENCODED_DISCARD_LEVELS * sizeof(opj_image_cmptparm_t)); + +        S32 numcomps = raw.getComponents(); +        S32 width = raw.getWidth(); +        S32 height = raw.getHeight(); + +        for (S32 c = 0; c < numcomps; c++) +        { +            cmptparm[c].prec = 8; +            cmptparm[c].bpp = 8; +            cmptparm[c].sgnd = 0; +            cmptparm[c].dx = parameters.subsampling_dx; +            cmptparm[c].dy = parameters.subsampling_dy; +            cmptparm[c].w = width; +            cmptparm[c].h = height; +        } + +        image = opj_image_create(numcomps, &cmptparm[0], OPJ_CLRSPC_SRGB); + +        image->x1 = width; +        image->y1 = height; + +        const U8 *src_datap = raw.getData(); + +        S32 i = 0; +        for (S32 y = height - 1; y >= 0; y--) +        { +            for (S32 x = 0; x < width; x++) +            { +                const U8 *pixel = src_datap + (y*width + x) * numcomps; +                for (S32 c = 0; c < numcomps; c++) +                { +                    image->comps[c].data[i] = *pixel; +                    pixel++; +                } +                i++; +            } +        } + +        // This likely works, but there seems to be an issue openjpeg side +        // check over after gixing that. + +        // De-interleave to component plane data +        /* +        switch (numcomps) +        { +        case 0: +        default: +            break; + +        case 1: +        { +            U32 rBitDepth = image->comps[0].bpp; +            U32 bytesPerPixel = rBitDepth >> 3; +            memcpy(image->comps[0].data, src, width * height * bytesPerPixel); +        } +        break; + +        case 2: +        { +            U32 rBitDepth = image->comps[0].bpp; +            U32 gBitDepth = image->comps[1].bpp; +            U32 totalBitDepth = rBitDepth + gBitDepth; +            U32 bytesPerPixel = totalBitDepth >> 3; +            U32 stride = width * bytesPerPixel; +            U32 offset = 0; +            for (S32 y = height - 1; y >= 0; y--) +            { +                const U8* component = src + (y * stride); +                for (S32 x = 0; x < width; x++) +                { +                    image->comps[0].data[offset] = *component++; +                    image->comps[1].data[offset] = *component++; +                    offset++; +                } +            } +        } +        break; + +        case 3: +        { +            U32 rBitDepth = image->comps[0].bpp; +            U32 gBitDepth = image->comps[1].bpp; +            U32 bBitDepth = image->comps[2].bpp; +            U32 totalBitDepth = rBitDepth + gBitDepth + bBitDepth; +            U32 bytesPerPixel = totalBitDepth >> 3; +            U32 stride = width * bytesPerPixel; +            U32 offset = 0; +            for (S32 y = height - 1; y >= 0; y--) +            { +                const U8* component = src + (y * stride); +                for (S32 x = 0; x < width; x++) +                { +                    image->comps[0].data[offset] = *component++; +                    image->comps[1].data[offset] = *component++; +                    image->comps[2].data[offset] = *component++; +                    offset++; +                } +            } +        } +        break; + + +        case 4: +        { +            U32 rBitDepth = image->comps[0].bpp; +            U32 gBitDepth = image->comps[1].bpp; +            U32 bBitDepth = image->comps[2].bpp; +            U32 aBitDepth = image->comps[3].bpp; + +            U32 totalBitDepth = rBitDepth + gBitDepth + bBitDepth + aBitDepth; +            U32 bytesPerPixel = totalBitDepth >> 3; + +            U32 stride = width * bytesPerPixel; +            U32 offset = 0; +            for (S32 y = height - 1; y >= 0; y--) +            { +                const U8* component = src + (y * stride); +                for (S32 x = 0; x < width; x++) +                { +                    image->comps[0].data[offset] = *component++; +                    image->comps[1].data[offset] = *component++; +                    image->comps[2].data[offset] = *component++; +                    image->comps[3].data[offset] = *component++; +                    offset++; +                } +            } +        } +        break; +        }*/ +    } + +    opj_image_t* getImage() { return image; } + +private: +    opj_cparameters_t   parameters; +    opj_event_mgr_t     event_mgr; +    opj_image_t*        image = nullptr; +    opj_codec_t*        encoder = nullptr; +    opj_stream_t*       stream = nullptr; +    char*               comment_text = nullptr; +}; -	/* catch events using our callbacks and give a local context */ -	opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, stderr);			 -	/* setup the decoder decoding parameters using user parameters */ -	opj_setup_decoder(dinfo, ¶meters); +LLImageJ2COJ::LLImageJ2COJ() +	: LLImageJ2CImpl() +{ +} -	/* open a byte stream */ -	cio = opj_cio_open((opj_common_ptr)dinfo, base.getData(), base.getDataSize()); -	/* decode the stream and fill the image structure */ -	image = opj_decode(dinfo, cio); +LLImageJ2COJ::~LLImageJ2COJ() +{ +} -	/* close the byte stream */ -	opj_cio_close(cio); +bool LLImageJ2COJ::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level, int* region) +{ +    base.mDiscardLevel = discard_level; +	return false; +} -	/* free remaining structures */ -	if(dinfo) -	{ -		opj_destroy_decompress(dinfo); -	} +bool LLImageJ2COJ::initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size, int precincts_size, int levels) +{ +	// No specific implementation for this method in the OpenJpeg case +	return false; +} -	if(!image) -	{ -		LL_WARNS() << "ERROR -> getMetadata: failed to decode image!" << LL_ENDL; -		return false; -	} +bool LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) +{ +    JPEG2KDecode decoder(0); + +    U32 image_channels = 0; +    S32 data_size = base.getDataSize(); +    S32 max_bytes = (base.getMaxBytes() ? base.getMaxBytes() : data_size); +    bool decoded = decoder.decode(base.getData(), max_bytes, &image_channels, base.mDiscardLevel); + +    // set correct channel count early so failed decodes don't miss it... +    S32 channels = (S32)image_channels - first_channel; +    channels = llmin(channels, max_channel_count); + +    if (!decoded) +    { +        // reset the channel count if necessary +        if (raw_image.getComponents() != channels) +        { +            raw_image.resize(raw_image.getWidth(), raw_image.getHeight(), S8(channels)); +        } + +        LL_DEBUGS("Texture") << "ERROR -> decodeImpl: failed to decode image!" << LL_ENDL; +        return true; // done +    } + +    opj_image_t *image = decoder.getImage(); + +    // Component buffers are allocated in an image width by height buffer. +    // The image placed in that buffer is ceil(width/2^factor) by +    // ceil(height/2^factor) and if the factor isn't zero it will be at the +    // top left of the buffer with black filled in the rest of the pixels. +    // It is integer math so the formula is written in ceildivpo2. +    // (Assuming all the components have the same width, height and +    // factor.) +    U32 comp_width = image->comps[0].w; // leave this unshifted by 'f' discard factor, the strides are always for the full buffer width +    U32 f = image->comps[0].factor; + +    // do size the texture to the mem we'll acrually use... +    U32 width = image->comps[0].w; +    U32 height = image->comps[0].h; + +    raw_image.resize(U16(width), U16(height), S8(channels)); + +    U8 *rawp = raw_image.getData(); + +    // first_channel is what channel to start copying from +    // dest is what channel to copy to.  first_channel comes from the +    // argument, dest always starts writing at channel zero. +    for (S32 comp = first_channel, dest = 0; comp < first_channel + channels; comp++, dest++) +    { +        llassert(image->comps[comp].data); +        if (image->comps[comp].data) +        { +            S32 offset = dest; +            for (S32 y = (height - 1); y >= 0; y--) +            { +                for (S32 x = 0; x < width; x++) +                { +                    rawp[offset] = image->comps[comp].data[y*comp_width + x]; +                    offset += channels; +                } +            } +        } +        else // Some rare OpenJPEG versions have this bug. +        { +            LL_DEBUGS("Texture") << "ERROR -> decodeImpl: failed! (OpenJPEG bug)" << LL_ENDL; +        } +    } + +    base.setDiscardLevel(f); + +    return true; // done +} -	// Copy image data into our raw image format (instead of the separate channel format -	S32 width = 0; -	S32 height = 0; -	S32 img_components = image->numcomps; -	width = image->x1 - image->x0; -	height = image->y1 - image->y0; -	base.setSize(width, height, img_components); +bool LLImageJ2COJ::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time, bool reversible) +{ +    JPEG2KEncode encode(comment_text, reversible); +    bool encoded = encode.encode(raw_image, base); +    if (encoded) +    { +        LL_WARNS() << "Openjpeg encoding implementation isn't complete, returning false" << LL_ENDL; +    } +    //return encoded; +    return false; +} -	/* free image data structure */ -	opj_image_destroy(image); -	return true; +bool LLImageJ2COJ::getMetadata(LLImageJ2C &base) +{ +    JPEG2KDecode decode(0); + +    S32 width = 0; +    S32 height = 0; +    S32 components = 0; +    S32 discard_level = 0; + +    U32 dataSize = base.getDataSize(); +    U8* data = base.getData(); +    bool header_read = decode.readHeader(data, dataSize, width, height, components, discard_level); +    if (!header_read) +    { +        return false; +    } + +    base.mDiscardLevel = discard_level; +    base.setSize(width, height, components); +    return true;  } diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 7d78ec9e3c..4fe83f0c45 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1825,9 +1825,9 @@ if (WINDOWS)        ${SHARED_LIB_STAGING_DIR}/Release/libcollada14dom22.dll        ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libcollada14dom22.dll        ${SHARED_LIB_STAGING_DIR}/Debug/libcollada14dom22-d.dll -      ${SHARED_LIB_STAGING_DIR}/Release/openjpeg.dll -      ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/openjpeg.dll -      ${SHARED_LIB_STAGING_DIR}/Debug/openjpegd.dll +      ${SHARED_LIB_STAGING_DIR}/Release/openjp2.dll +      ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/openjp2.dll +      ${SHARED_LIB_STAGING_DIR}/Debug/openjp2.dll        ${SHARED_LIB_STAGING_DIR}/Release/libhunspell.dll        ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libhunspell.dll        ${SHARED_LIB_STAGING_DIR}/Debug/libhunspell.dll diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index de5ac5ed3d..a1d2d12779 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -518,7 +518,7 @@ class WindowsManifest(ViewerManifest):                  self.path("alut.dll")              # For textures -            self.path("openjpeg.dll") +            self.path("openjp2.dll")              # Uriparser              self.path("uriparser.dll") @@ -1498,7 +1498,7 @@ class Linux_i686_Manifest(LinuxManifest):              self.path("libdirectfb-1.*.so.*")              self.path("libfusion-1.*.so.*")              self.path("libdirect-1.*.so.*") -            self.path("libopenjpeg.so*") +            self.path("libopenjp2.so*")              self.path("libdirectfb-1.4.so.5")              self.path("libfusion-1.4.so.5")              self.path("libdirect-1.4.so.5*") | 
