From 8430328043c140fa8ccafee21c57856238e2d257 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 29 Sep 2012 17:47:15 +0200 Subject: [PATCH] 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). --- indra/llimage/llimage.cpp | 1 + indra/newview/llfloaterimagepreview.cpp | 16 +++++++++ indra/newview/llviewermenufile.cpp | 34 ++++++++++++------- indra/newview/llviewertexturelist.cpp | 40 ++++++++++++++++++----- indra/newview/llviewertexturelist.h | 1 + indra/plugins/filepicker/llfilepicker.cpp | 10 ++++-- 6 files changed, 80 insertions(+), 22 deletions(-) diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index c3f4878ee..66217f902 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -1239,6 +1239,7 @@ file_extensions[] = { { "bmp", IMG_CODEC_BMP }, { "tga", IMG_CODEC_TGA }, + { "j2k", IMG_CODEC_J2C }, { "j2c", IMG_CODEC_J2C }, { "jp2", IMG_CODEC_J2C }, { "texture", IMG_CODEC_J2C }, diff --git a/indra/newview/llfloaterimagepreview.cpp b/indra/newview/llfloaterimagepreview.cpp index d32e08847..edb961685 100644 --- a/indra/newview/llfloaterimagepreview.cpp +++ b/indra/newview/llfloaterimagepreview.cpp @@ -38,6 +38,7 @@ #include "llimagetga.h" #include "llimagejpeg.h" #include "llimagepng.h" +#include "llimagej2c.h" #include "llagent.h" #include "llbutton.h" @@ -423,6 +424,21 @@ bool LLFloaterImagePreview::loadImage(const std::string& src_filename) } } break; + case IMG_CODEC_J2C: + { + LLPointer j2c_image = new LLImageJ2C; + + if (!j2c_image->load(src_filename)) + { + return false; + } + + if (!j2c_image->decode(raw_image, 0.0f)) + { + return false; + } + } + break; default: return false; } diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 11c15d9d1..fc795f8fb 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -651,9 +651,10 @@ void upload_new_resource(const std::string& src_filename, std::string name, LLAssetStorage::LLStoreAssetCallback callback, S32 expected_upload_cost, void *userdata) -{ +{ // Generate the temporary UUID. std::string filename = gDirUtilp->getTempFilename(); + bool created_temp_file = false; LLTransactionID tid; 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); 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) { // It's an image file, the upload procedure is the same for all asset_type = LLAssetType::AT_TEXTURE; + created_temp_file = true; if (!LLViewerTextureList::createUploadFile(src_filename, filename, codec)) { 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; encode_result = encode_vorbis_file(src_filename, filename); + created_temp_file = true; 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 LLFILE* out = LLFile::fopen(filename, "wb"); /* Flawfinder: ignore */ + created_temp_file = true; if (out) { 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; filename = src_filename; } - else if(exten == "j2k" || exten == "jp2" || exten == "j2c") - { - asset_type = LLAssetType::AT_TEXTURE; - filename = src_filename; - } // else { @@ -934,11 +947,10 @@ void upload_new_resource(const std::string& src_filename, std::string name, LLSD args; args["ERROR_MESSAGE"] = error_message; LLNotificationsUtil::add("ErrorMessage", args); - if(LLFile::remove(filename) == -1) - { - lldebugs << "unable to remove temp file" << llendl; - } - //AIFIXME? LLFilePicker::instance().reset(); + } + if (created_temp_file && LLFile::remove(filename) == -1) + { + lldebugs << "unable to remove temp file" << llendl; } } // diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index d685b951e..3c89714c0 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -1036,54 +1036,78 @@ void LLViewerTextureList::decodeAllImages(F32 max_time) BOOL LLViewerTextureList::createUploadFile(const std::string& filename, const std::string& out_filename, const U8 codec) -{ +{ // Load the image LLPointer image = LLImageFormatted::createFromType(codec); 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; } 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; } // Decompress or expand it in a raw image structure LLPointer raw_image = new LLImageRaw; 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; } // Check the image constraints 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; } // Convert to j2c (JPEG2000) and save the file locally LLPointer compressedImage = convertToUploadFile(raw_image); 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; return FALSE; } 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; 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 LLPointer integrity_test = new LLImageJ2C; 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; 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; } diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h index fa414ad5f..043a48cbd 100644 --- a/indra/newview/llviewertexturelist.h +++ b/indra/newview/llviewertexturelist.h @@ -68,6 +68,7 @@ class LLViewerTextureList public: 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 convertToUploadFile(LLPointer raw_image); static void processImageNotInDatabase( LLMessageSystem *msg, void **user_data ); static S32 calcMaxTextureRAM(); diff --git a/indra/plugins/filepicker/llfilepicker.cpp b/indra/plugins/filepicker/llfilepicker.cpp index 8342882c8..ed86e0f6e 100644 --- a/indra/plugins/filepicker/llfilepicker.cpp +++ b/indra/plugins/filepicker/llfilepicker.cpp @@ -729,12 +729,15 @@ Boolean LLFilePickerBase::navOpenFilterProc(AEDesc *theItem, void *info, void *c if (fileInfo.filetype != 'JPEG' && fileInfo.filetype != 'JPG ' && fileInfo.filetype != 'BMP ' && fileInfo.filetype != 'TGA ' && fileInfo.filetype != 'BMPf' && fileInfo.filetype != 'TPIC' && - fileInfo.filetype != 'PNG ' && + fileInfo.filetype != 'PNG ' && fileInfo.filetype != 'JP2' && (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("jpeg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && CFStringCompare(fileInfo.extension, CFSTR("jpg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && CFStringCompare(fileInfo.extension, CFSTR("bmp"), 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; @@ -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/png"); 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); return filtername; }