Add support for uploading JP2 files.
Recognizes .jp2, .j2c and .j2k extensions. Adds image/jp2 files to file picker image filter (windows and Mac, windows apparently already showed them). Show preview for jpeg 2000 files. Fixes error reporting for failed image uploads. Enforces a power-of-two size for jpeg 2000 files (seemed to make sense to do that).
This commit is contained in:
@@ -1239,6 +1239,7 @@ file_extensions[] =
|
|||||||
{
|
{
|
||||||
{ "bmp", IMG_CODEC_BMP },
|
{ "bmp", IMG_CODEC_BMP },
|
||||||
{ "tga", IMG_CODEC_TGA },
|
{ "tga", IMG_CODEC_TGA },
|
||||||
|
{ "j2k", IMG_CODEC_J2C },
|
||||||
{ "j2c", IMG_CODEC_J2C },
|
{ "j2c", IMG_CODEC_J2C },
|
||||||
{ "jp2", IMG_CODEC_J2C },
|
{ "jp2", IMG_CODEC_J2C },
|
||||||
{ "texture", IMG_CODEC_J2C },
|
{ "texture", IMG_CODEC_J2C },
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
#include "llimagetga.h"
|
#include "llimagetga.h"
|
||||||
#include "llimagejpeg.h"
|
#include "llimagejpeg.h"
|
||||||
#include "llimagepng.h"
|
#include "llimagepng.h"
|
||||||
|
#include "llimagej2c.h"
|
||||||
|
|
||||||
#include "llagent.h"
|
#include "llagent.h"
|
||||||
#include "llbutton.h"
|
#include "llbutton.h"
|
||||||
@@ -423,6 +424,21 @@ bool LLFloaterImagePreview::loadImage(const std::string& src_filename)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case IMG_CODEC_J2C:
|
||||||
|
{
|
||||||
|
LLPointer<LLImageJ2C> j2c_image = new LLImageJ2C;
|
||||||
|
|
||||||
|
if (!j2c_image->load(src_filename))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!j2c_image->decode(raw_image, 0.0f))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -651,9 +651,10 @@ void upload_new_resource(const std::string& src_filename, std::string name,
|
|||||||
LLAssetStorage::LLStoreAssetCallback callback,
|
LLAssetStorage::LLStoreAssetCallback callback,
|
||||||
S32 expected_upload_cost,
|
S32 expected_upload_cost,
|
||||||
void *userdata)
|
void *userdata)
|
||||||
{
|
{
|
||||||
// Generate the temporary UUID.
|
// Generate the temporary UUID.
|
||||||
std::string filename = gDirUtilp->getTempFilename();
|
std::string filename = gDirUtilp->getTempFilename();
|
||||||
|
bool created_temp_file = false;
|
||||||
LLTransactionID tid;
|
LLTransactionID tid;
|
||||||
LLAssetID uuid;
|
LLAssetID uuid;
|
||||||
|
|
||||||
@@ -678,10 +679,25 @@ void upload_new_resource(const std::string& src_filename, std::string name,
|
|||||||
upload_error(error_message, "NofileExtension", filename, args);
|
upload_error(error_message, "NofileExtension", filename, args);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (codec == IMG_CODEC_J2C)
|
||||||
|
{
|
||||||
|
asset_type = LLAssetType::AT_TEXTURE;
|
||||||
|
if (!LLViewerTextureList::verifyUploadFile(src_filename, codec))
|
||||||
|
{
|
||||||
|
error_message = llformat( "Problem with file %s:\n\n%s\n",
|
||||||
|
src_filename.c_str(), LLImage::getLastError().c_str());
|
||||||
|
args["FILE"] = src_filename;
|
||||||
|
args["ERROR"] = LLImage::getLastError();
|
||||||
|
upload_error(error_message, "ProblemWithFile", filename, args);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
filename = src_filename;
|
||||||
|
}
|
||||||
else if (codec != IMG_CODEC_INVALID)
|
else if (codec != IMG_CODEC_INVALID)
|
||||||
{
|
{
|
||||||
// It's an image file, the upload procedure is the same for all
|
// It's an image file, the upload procedure is the same for all
|
||||||
asset_type = LLAssetType::AT_TEXTURE;
|
asset_type = LLAssetType::AT_TEXTURE;
|
||||||
|
created_temp_file = true;
|
||||||
if (!LLViewerTextureList::createUploadFile(src_filename, filename, codec))
|
if (!LLViewerTextureList::createUploadFile(src_filename, filename, codec))
|
||||||
{
|
{
|
||||||
error_message = llformat( "Problem with file %s:\n\n%s\n",
|
error_message = llformat( "Problem with file %s:\n\n%s\n",
|
||||||
@@ -700,6 +716,7 @@ void upload_new_resource(const std::string& src_filename, std::string name,
|
|||||||
llinfos << "Attempting to encode wav as an ogg file" << llendl;
|
llinfos << "Attempting to encode wav as an ogg file" << llendl;
|
||||||
|
|
||||||
encode_result = encode_vorbis_file(src_filename, filename);
|
encode_result = encode_vorbis_file(src_filename, filename);
|
||||||
|
created_temp_file = true;
|
||||||
|
|
||||||
if (LLVORBISENC_NOERR != encode_result)
|
if (LLVORBISENC_NOERR != encode_result)
|
||||||
{
|
{
|
||||||
@@ -815,6 +832,7 @@ void upload_new_resource(const std::string& src_filename, std::string name,
|
|||||||
|
|
||||||
// copy the file's data segment into another file for uploading
|
// copy the file's data segment into another file for uploading
|
||||||
LLFILE* out = LLFile::fopen(filename, "wb"); /* Flawfinder: ignore */
|
LLFILE* out = LLFile::fopen(filename, "wb"); /* Flawfinder: ignore */
|
||||||
|
created_temp_file = true;
|
||||||
if (out)
|
if (out)
|
||||||
{
|
{
|
||||||
while((readbytes = fread(buf, 1, 16384, in))) /* Flawfinder: ignore */
|
while((readbytes = fread(buf, 1, 16384, in))) /* Flawfinder: ignore */
|
||||||
@@ -854,11 +872,6 @@ void upload_new_resource(const std::string& src_filename, std::string name,
|
|||||||
asset_type = LLAssetType::AT_ANIMATION;
|
asset_type = LLAssetType::AT_ANIMATION;
|
||||||
filename = src_filename;
|
filename = src_filename;
|
||||||
}
|
}
|
||||||
else if(exten == "j2k" || exten == "jp2" || exten == "j2c")
|
|
||||||
{
|
|
||||||
asset_type = LLAssetType::AT_TEXTURE;
|
|
||||||
filename = src_filename;
|
|
||||||
}
|
|
||||||
// </edit>
|
// </edit>
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -934,11 +947,10 @@ void upload_new_resource(const std::string& src_filename, std::string name,
|
|||||||
LLSD args;
|
LLSD args;
|
||||||
args["ERROR_MESSAGE"] = error_message;
|
args["ERROR_MESSAGE"] = error_message;
|
||||||
LLNotificationsUtil::add("ErrorMessage", args);
|
LLNotificationsUtil::add("ErrorMessage", args);
|
||||||
if(LLFile::remove(filename) == -1)
|
}
|
||||||
{
|
if (created_temp_file && LLFile::remove(filename) == -1)
|
||||||
lldebugs << "unable to remove temp file" << llendl;
|
{
|
||||||
}
|
lldebugs << "unable to remove temp file" << llendl;
|
||||||
//AIFIXME? LLFilePicker::instance().reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// <edit>
|
// <edit>
|
||||||
|
|||||||
@@ -1036,54 +1036,78 @@ void LLViewerTextureList::decodeAllImages(F32 max_time)
|
|||||||
BOOL LLViewerTextureList::createUploadFile(const std::string& filename,
|
BOOL LLViewerTextureList::createUploadFile(const std::string& filename,
|
||||||
const std::string& out_filename,
|
const std::string& out_filename,
|
||||||
const U8 codec)
|
const U8 codec)
|
||||||
{
|
{
|
||||||
// Load the image
|
// Load the image
|
||||||
LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec);
|
LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec);
|
||||||
if (image.isNull())
|
if (image.isNull())
|
||||||
{
|
{
|
||||||
image->setLastError("Couldn't open the image to be uploaded.");
|
LLImage::setLastError("Couldn't open the image to be uploaded.");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
if (!image->load(filename))
|
if (!image->load(filename))
|
||||||
{
|
{
|
||||||
image->setLastError("Couldn't load the image to be uploaded.");
|
LLImage::setLastError("Couldn't load the image to be uploaded.");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
// Decompress or expand it in a raw image structure
|
// Decompress or expand it in a raw image structure
|
||||||
LLPointer<LLImageRaw> raw_image = new LLImageRaw;
|
LLPointer<LLImageRaw> raw_image = new LLImageRaw;
|
||||||
if (!image->decode(raw_image, 0.0f))
|
if (!image->decode(raw_image, 0.0f))
|
||||||
{
|
{
|
||||||
image->setLastError("Couldn't decode the image to be uploaded.");
|
LLImage::setLastError("Couldn't decode the image to be uploaded.");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
// Check the image constraints
|
// Check the image constraints
|
||||||
if ((image->getComponents() != 3) && (image->getComponents() != 4))
|
if ((image->getComponents() != 3) && (image->getComponents() != 4))
|
||||||
{
|
{
|
||||||
image->setLastError("Image files with less than 3 or more than 4 components are not supported.");
|
LLImage::setLastError("Image files with less than 3 or more than 4 components are not supported.");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
// Convert to j2c (JPEG2000) and save the file locally
|
// Convert to j2c (JPEG2000) and save the file locally
|
||||||
LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image);
|
LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image);
|
||||||
if (compressedImage.isNull())
|
if (compressedImage.isNull())
|
||||||
{
|
{
|
||||||
image->setLastError("Couldn't convert the image to jpeg2000.");
|
LLImage::setLastError("Couldn't convert the image to jpeg2000.");
|
||||||
llinfos << "Couldn't convert to j2c, file : " << filename << llendl;
|
llinfos << "Couldn't convert to j2c, file : " << filename << llendl;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
if (!compressedImage->save(out_filename))
|
if (!compressedImage->save(out_filename))
|
||||||
{
|
{
|
||||||
image->setLastError("Couldn't create the jpeg2000 image for upload.");
|
LLImage::setLastError("Couldn't create the jpeg2000 image for upload.");
|
||||||
llinfos << "Couldn't create output file : " << out_filename << llendl;
|
llinfos << "Couldn't create output file : " << out_filename << llendl;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
return verifyUploadFile(out_filename, codec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool positive_power_of_two(int dim)
|
||||||
|
{
|
||||||
|
return dim > 0 && !(dim & (dim - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL LLViewerTextureList::verifyUploadFile(const std::string& out_filename, const U8 codec)
|
||||||
|
{
|
||||||
// Test to see if the encode and save worked
|
// Test to see if the encode and save worked
|
||||||
LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C;
|
LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C;
|
||||||
if (!integrity_test->loadAndValidate( out_filename ))
|
if (!integrity_test->loadAndValidate( out_filename ))
|
||||||
{
|
{
|
||||||
image->setLastError("The created jpeg2000 image is corrupt.");
|
LLImage::setLastError(std::string("The ") + ((codec == IMG_CODEC_J2C) ? "" : "created ") + "jpeg2000 image is corrupt: " + LLImage::getLastError());
|
||||||
llinfos << "Image file : " << out_filename << " is corrupt" << llendl;
|
llinfos << "Image file : " << out_filename << " is corrupt" << llendl;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
if (codec == IMG_CODEC_J2C)
|
||||||
|
{
|
||||||
|
if (integrity_test->getComponents() < 3 || integrity_test->getComponents() > 4)
|
||||||
|
{
|
||||||
|
LLImage::setLastError("Image files with less than 3 or more than 4 components are not supported.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
else if (!positive_power_of_two(integrity_test->getWidth()) ||
|
||||||
|
!positive_power_of_two(integrity_test->getHeight()))
|
||||||
|
{
|
||||||
|
LLImage::setLastError("The width or height is not a (positive) power of two.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ class LLViewerTextureList
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
static BOOL createUploadFile(const std::string& filename, const std::string& out_filename, const U8 codec);
|
static BOOL createUploadFile(const std::string& filename, const std::string& out_filename, const U8 codec);
|
||||||
|
static BOOL verifyUploadFile(const std::string& out_filename, const U8 codec);
|
||||||
static LLPointer<LLImageJ2C> convertToUploadFile(LLPointer<LLImageRaw> raw_image);
|
static LLPointer<LLImageJ2C> convertToUploadFile(LLPointer<LLImageRaw> raw_image);
|
||||||
static void processImageNotInDatabase( LLMessageSystem *msg, void **user_data );
|
static void processImageNotInDatabase( LLMessageSystem *msg, void **user_data );
|
||||||
static S32 calcMaxTextureRAM();
|
static S32 calcMaxTextureRAM();
|
||||||
|
|||||||
@@ -729,12 +729,15 @@ Boolean LLFilePickerBase::navOpenFilterProc(AEDesc *theItem, void *info, void *c
|
|||||||
if (fileInfo.filetype != 'JPEG' && fileInfo.filetype != 'JPG ' &&
|
if (fileInfo.filetype != 'JPEG' && fileInfo.filetype != 'JPG ' &&
|
||||||
fileInfo.filetype != 'BMP ' && fileInfo.filetype != 'TGA ' &&
|
fileInfo.filetype != 'BMP ' && fileInfo.filetype != 'TGA ' &&
|
||||||
fileInfo.filetype != 'BMPf' && fileInfo.filetype != 'TPIC' &&
|
fileInfo.filetype != 'BMPf' && fileInfo.filetype != 'TPIC' &&
|
||||||
fileInfo.filetype != 'PNG ' &&
|
fileInfo.filetype != 'PNG ' && fileInfo.filetype != 'JP2' &&
|
||||||
(fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("jpeg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
|
(fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("jpeg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
|
||||||
CFStringCompare(fileInfo.extension, CFSTR("jpg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
|
CFStringCompare(fileInfo.extension, CFSTR("jpg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
|
||||||
CFStringCompare(fileInfo.extension, CFSTR("bmp"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
|
CFStringCompare(fileInfo.extension, CFSTR("bmp"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
|
||||||
CFStringCompare(fileInfo.extension, CFSTR("tga"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
|
CFStringCompare(fileInfo.extension, CFSTR("tga"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
|
||||||
CFStringCompare(fileInfo.extension, CFSTR("png"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
|
CFStringCompare(fileInfo.extension, CFSTR("png"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
|
||||||
|
CFStringCompare(fileInfo.extension, CFSTR("jp2"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
|
||||||
|
CFStringCompare(fileInfo.extension, CFSTR("j2k"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
|
||||||
|
CFStringCompare(fileInfo.extension, CFSTR("j2c"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
result = false;
|
result = false;
|
||||||
@@ -1353,7 +1356,8 @@ static std::string add_imageload_filter_to_gtkchooser(GtkWindow *picker)
|
|||||||
gtk_file_filter_add_mime_type(gfilter, "image/jpeg");
|
gtk_file_filter_add_mime_type(gfilter, "image/jpeg");
|
||||||
gtk_file_filter_add_mime_type(gfilter, "image/png");
|
gtk_file_filter_add_mime_type(gfilter, "image/png");
|
||||||
gtk_file_filter_add_mime_type(gfilter, "image/bmp");
|
gtk_file_filter_add_mime_type(gfilter, "image/bmp");
|
||||||
std::string filtername = LLTrans::getString("image_files") + " (*.tga; *.bmp; *.jpg; *.png)";
|
gtk_file_filter_add_mime_type(gfilter, "image/jp2");
|
||||||
|
std::string filtername = LLTrans::getString("image_files") + " (*.tga; *.bmp; *.jpg; *.png; *.jp2; *.j2k; *.j2c)";
|
||||||
add_common_filters_to_gtkchooser(gfilter, picker, filtername);
|
add_common_filters_to_gtkchooser(gfilter, picker, filtername);
|
||||||
return filtername;
|
return filtername;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user