From cfb69846f1e8309ed86d4a18eb26a889f6dbaccc Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Sat, 9 Sep 2023 00:00:22 +0300
Subject: SL-20261 Allow and resize existing textures as necessary for
 thumbnails

---
 indra/newview/llfloaterchangeitemthumbnail.cpp     | 132 ++++++++++++++++++++-
 indra/newview/llfloaterchangeitemthumbnail.h       |   9 +-
 indra/newview/llfloatersimplesnapshot.cpp          |  15 +++
 indra/newview/llfloatersimplesnapshot.h            |   1 +
 indra/newview/llsnapshotlivepreview.cpp            |  32 +----
 indra/newview/lltexturectrl.cpp                    |   5 +-
 indra/newview/lltexturectrl.h                      |   2 +-
 indra/newview/llviewertexturelist.cpp              |  39 ++++++
 indra/newview/llviewertexturelist.h                |   4 +
 .../skins/default/xui/en/floater_texture_ctrl.xml  |   2 +-
 10 files changed, 197 insertions(+), 44 deletions(-)

(limited to 'indra/newview')

diff --git a/indra/newview/llfloaterchangeitemthumbnail.cpp b/indra/newview/llfloaterchangeitemthumbnail.cpp
index 77212507ab..780130039b 100644
--- a/indra/newview/llfloaterchangeitemthumbnail.cpp
+++ b/indra/newview/llfloaterchangeitemthumbnail.cpp
@@ -512,6 +512,13 @@ void LLFloaterChangeItemThumbnail::onPasteFromClipboard(void *userdata)
                 asset_id = potential_uuid;
             }
         }
+
+        LLInventoryObject* obj = self->getInventoryObject();
+        if (obj && obj->getThumbnailUUID() == asset_id)
+        {
+            // nothing to do
+            return;
+        }
         if (asset_id.notNull())
         {
             self->assignAndValidateAsset(asset_id);
@@ -547,6 +554,8 @@ struct ImageLoadedData
     LLUUID mObjectId;
     LLHandle<LLFloater> mFloaterHandle;
     bool mSilent;
+    // Keep image reference to prevent deletion on timeout
+    LLPointer<LLViewerFetchedTexture> mTexturep;
 };
 
 void LLFloaterChangeItemThumbnail::assignAndValidateAsset(const LLUUID &asset_id, bool silent)
@@ -576,8 +585,9 @@ void LLFloaterChangeItemThumbnail::assignAndValidateAsset(const LLUUID &asset_id
         data->mThumbnailId = asset_id;
         data->mFloaterHandle = getHandle();
         data->mSilent = silent;
+        data->mTexturep = texturep;
 
-        texturep->setLoadedCallback(onImageLoaded,
+        texturep->setLoadedCallback(onImageDataLoaded,
             MAX_DISCARD_LEVEL, // Don't need full image, just size data
             FALSE,
             FALSE,
@@ -636,7 +646,7 @@ bool LLFloaterChangeItemThumbnail::validateAsset(const LLUUID &asset_id)
 }
 
 //static
-void LLFloaterChangeItemThumbnail::onImageLoaded(
+void LLFloaterChangeItemThumbnail::onImageDataLoaded(
     BOOL success,
     LLViewerFetchedTexture *src_vi,
     LLImageRaw* src,
@@ -678,6 +688,45 @@ void LLFloaterChangeItemThumbnail::onImageLoaded(
     delete data;
 }
 
+//static
+void LLFloaterChangeItemThumbnail::onFullImageLoaded(
+    BOOL success,
+    LLViewerFetchedTexture* src_vi,
+    LLImageRaw* src,
+    LLImageRaw* aux_src,
+    S32 discard_level,
+    BOOL final,
+    void* userdata)
+{
+    if (!userdata) return;
+
+    if (!final && success) return; //not done yet
+
+    ImageLoadedData* data = (ImageLoadedData*)userdata;
+
+    if (success)
+    {
+        if (src_vi->getFullWidth() != src_vi->getFullHeight()
+            || src_vi->getFullWidth() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN)
+        {
+            if (!data->mSilent)
+            {
+                LLNotificationsUtil::add("ThumbnailDimentionsLimit");
+            }
+        }
+        else if (src_vi->getFullWidth() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX)
+        {
+            LLFloaterSimpleSnapshot::uploadThumbnail(src, data->mObjectId, LLUUID::null);
+        }
+        else
+        {
+            setThumbnailId(data->mThumbnailId, data->mObjectId);
+        }
+    }
+
+    delete data;
+}
+
 void LLFloaterChangeItemThumbnail::showTexturePicker(const LLUUID &thumbnail_id)
 {
     // show hourglass cursor when loading inventory window
@@ -725,8 +774,7 @@ void LLFloaterChangeItemThumbnail::showTexturePicker(const LLUUID &thumbnail_id)
             texture_floaterp->setBakeTextureEnabled(FALSE);
             texture_floaterp->setCanApplyImmediately(false);
             texture_floaterp->setCanApply(false, true, false /*Hide 'preview disabled'*/);
-            texture_floaterp->setDimentionsLimits(LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX,
-                                                 LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN);
+            texture_floaterp->setMinDimentionsLimits(LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN);
 
             addDependentFloater(texture_floaterp);
         }
@@ -743,14 +791,86 @@ void LLFloaterChangeItemThumbnail::onTexturePickerCommit()
     if (floaterp)
     {
         LLUUID asset_id = floaterp->getAssetID();
-        if (validateAsset(asset_id))
+
+        if (asset_id.isNull())
         {
             setThumbnailId(asset_id);
+            return;
         }
-        else
+
+        LLInventoryObject* obj = getInventoryObject();
+        if (obj && obj->getThumbnailUUID() == asset_id)
+        {
+            // nothing to do
+            return;
+        }
+
+        LLPointer<LLViewerFetchedTexture> texturep = LLViewerTextureManager::findFetchedTexture(asset_id, TEX_LIST_STANDARD);
+        if (!texturep)
+        {
+            LL_WARNS() << "Image " << asset_id << " doesn't exist" << LL_ENDL;
+            return;
+        }
+
+        if (texturep->isMissingAsset())
+        {
+            LL_WARNS() << "Image " << asset_id << " is missing" << LL_ENDL;
+            return;
+        }
+
+        if (texturep->getFullWidth() != texturep->getFullHeight())
+        {
+            LLNotificationsUtil::add("ThumbnailDimentionsLimit");
+            return;
+        }
+
+        if (texturep->getFullWidth() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN
+            && texturep->getFullWidth() > 0)
         {
             LLNotificationsUtil::add("ThumbnailDimentionsLimit");
+            return;
+        }
+
+        if (texturep->getFullWidth() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX
+            || texturep->getFullWidth() == 0)
+        {
+            if (texturep->isFullyLoaded()
+                && (texturep->getCachedRawImageLevel() == 0 || texturep->getRawImageLevel() == 0)
+                && (texturep->isCachedRawImageReady() || texturep->isRawImageValid()))
+            {
+                if (texturep->isRawImageValid())
+                {
+                    LLFloaterSimpleSnapshot::uploadThumbnail(texturep->getRawImage(), mItemId, mTaskId);
+                }
+                else
+                {
+                    LLFloaterSimpleSnapshot::uploadThumbnail(texturep->getCachedRawImage(), mItemId, mTaskId);
+                }
+            }
+            else
+            {
+                ImageLoadedData* data = new ImageLoadedData();
+                data->mObjectId = mItemId;
+                data->mThumbnailId = asset_id;
+                data->mFloaterHandle = getHandle();
+                data->mSilent = false;
+                data->mTexturep = texturep;
+
+                texturep->setBoostLevel(LLGLTexture::BOOST_PREVIEW);
+                texturep->setMinDiscardLevel(0);
+                texturep->setLoadedCallback(onFullImageLoaded,
+                                            0, // Need best quality
+                                            TRUE,
+                                            FALSE,
+                                            (void*)data,
+                                            NULL,
+                                            FALSE);
+                texturep->forceToSaveRawImage(0);
+            }
+            return;
         }
+
+        setThumbnailId(asset_id);
     }
 }
 
diff --git a/indra/newview/llfloaterchangeitemthumbnail.h b/indra/newview/llfloaterchangeitemthumbnail.h
index 02f934337b..a91e9b8ee9 100644
--- a/indra/newview/llfloaterchangeitemthumbnail.h
+++ b/indra/newview/llfloaterchangeitemthumbnail.h
@@ -83,13 +83,20 @@ private:
     static void onRemovalConfirmation(const LLSD& notification, const LLSD& response, LLHandle<LLFloater> handle);
 
     void assignAndValidateAsset(const LLUUID &asset_id, bool silent = false);
-    static void onImageLoaded(BOOL success,
+    static void onImageDataLoaded(BOOL success,
         LLViewerFetchedTexture *src_vi,
         LLImageRaw* src,
         LLImageRaw* aux_src,
         S32 discard_level,
         BOOL final,
         void* userdata);
+    static void onFullImageLoaded(BOOL success,
+                                  LLViewerFetchedTexture* src_vi,
+                                  LLImageRaw* src,
+                                  LLImageRaw* aux_src,
+                                  S32 discard_level,
+                                  BOOL final,
+                                  void* userdata);
 
     void showTexturePicker(const LLUUID &thumbnail_id);
     void onTexturePickerCommit();
diff --git a/indra/newview/llfloatersimplesnapshot.cpp b/indra/newview/llfloatersimplesnapshot.cpp
index 757ac605e3..58604c5628 100644
--- a/indra/newview/llfloatersimplesnapshot.cpp
+++ b/indra/newview/llfloatersimplesnapshot.cpp
@@ -397,6 +397,21 @@ void LLFloaterSimpleSnapshot::uploadThumbnail(const std::string &file_path, cons
     uploadImageUploadFile(temp_file, inventory_id, task_id);
 }
 
+// static
+void LLFloaterSimpleSnapshot::uploadThumbnail(LLPointer<LLImageRaw> raw_image, const LLUUID& inventory_id, const LLUUID& task_id)
+{
+    std::string temp_file = gDirUtilp->getTempFilename();
+    if (!LLViewerTextureList::createUploadFile(raw_image, temp_file, THUMBNAIL_SNAPSHOT_DIM_MAX, THUMBNAIL_SNAPSHOT_DIM_MIN))
+    {
+        LLSD notif_args;
+        notif_args["REASON"] = LLImage::getLastError().c_str();
+        LLNotificationsUtil::add("CannotUploadTexture", notif_args);
+        LL_WARNS("Thumbnail") << "Failed to upload thumbnail for " << inventory_id << " " << task_id << ", reason: " << notif_args["REASON"].asString() << LL_ENDL;
+        return;
+    }
+    uploadImageUploadFile(temp_file, inventory_id, task_id);
+}
+
 // static
 void LLFloaterSimpleSnapshot::uploadImageUploadFile(const std::string &temp_file, const LLUUID &inventory_id, const LLUUID &task_id)
 {
diff --git a/indra/newview/llfloatersimplesnapshot.h b/indra/newview/llfloatersimplesnapshot.h
index a2bf2946d4..91a81ee5c3 100644
--- a/indra/newview/llfloatersimplesnapshot.h
+++ b/indra/newview/llfloatersimplesnapshot.h
@@ -63,6 +63,7 @@ public:
 
     void postSave();
     static void uploadThumbnail(const std::string &file_path, const LLUUID &inventory_id, const LLUUID &task_id);
+    static void uploadThumbnail(LLPointer<LLImageRaw> raw_image, const LLUUID& inventory_id, const LLUUID& task_id);
 
     class Impl;
     friend class Impl;
diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp
index 37ebcd1266..b7a1832b17 100644
--- a/indra/newview/llsnapshotlivepreview.cpp
+++ b/indra/newview/llsnapshotlivepreview.cpp
@@ -876,37 +876,7 @@ LLPointer<LLImageRaw> LLSnapshotLivePreview::getEncodedImage()
 
 bool LLSnapshotLivePreview::createUploadFile(const std::string &out_filename, const S32 max_image_dimentions, const S32 min_image_dimentions)
 {
-    // make a copy, since convertToUploadFile modifies raw image
-    LLPointer<LLImageRaw> raw_image = new LLImageRaw(
-        mPreviewImage->getData(),
-        mPreviewImage->getWidth(),
-        mPreviewImage->getHeight(),
-        mPreviewImage->getComponents());
-
-    LLPointer<LLImageJ2C> compressedImage = LLViewerTextureList::convertToUploadFile(raw_image, max_image_dimentions);
-    if (compressedImage->getWidth() < min_image_dimentions || compressedImage->getHeight() < min_image_dimentions)
-    {
-        std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx",
-            min_image_dimentions,
-            min_image_dimentions,
-            compressedImage->getWidth(),
-            compressedImage->getHeight());
-        compressedImage->setLastError(reason);
-        return FALSE;
-    }
-    if (compressedImage.isNull())
-    {
-        compressedImage->setLastError("Couldn't convert the image to jpeg2000.");
-        LL_INFOS() << "Couldn't convert to j2c, file : " << out_filename << LL_ENDL;
-        return false;
-    }
-    if (!compressedImage->save(out_filename))
-    {
-        compressedImage->setLastError("Couldn't create the jpeg2000 image for upload.");
-        LL_INFOS() << "Couldn't create output file : " << out_filename << LL_ENDL;
-        return false;
-    }
-    return true;
+    return LLViewerTextureList::createUploadFile(mPreviewImage, out_filename, max_image_dimentions, min_image_dimentions);
 }
 
 // We actually estimate the data size so that we do not require actual compression when showing the preview
diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp
index a746744e53..0c3730d084 100644
--- a/indra/newview/lltexturectrl.cpp
+++ b/indra/newview/lltexturectrl.cpp
@@ -1197,15 +1197,12 @@ void LLFloaterTexturePicker::setCanApply(bool can_preview, bool can_apply, bool
 	mPreviewSettingChanged = true;
 }
 
-void LLFloaterTexturePicker::setDimentionsLimits(S32 max_dim, S32 min_dim)
+void LLFloaterTexturePicker::setMinDimentionsLimits(S32 min_dim)
 {
-    mMaxDim = max_dim;
     mMinDim = min_dim;
 
     std::string formatted_dims = llformat("%dx%d", mMinDim, mMinDim);
     mResolutionWarning->setTextArg("[MINTEXDIM]", formatted_dims);
-    formatted_dims = llformat("%dx%d", mMaxDim, mMaxDim);
-    mResolutionWarning->setTextArg("[MAXTEXDIM]", formatted_dims);
 }
 
 void LLFloaterTexturePicker::onFilterEdit(const std::string& search_string )
diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h
index 60543191b6..7239b97552 100644
--- a/indra/newview/lltexturectrl.h
+++ b/indra/newview/lltexturectrl.h
@@ -323,7 +323,7 @@ public:
 	void onFilterEdit(const std::string& search_string);
 
 	void setCanApply(bool can_preview, bool can_apply, bool inworld_image = true);
-    void setDimentionsLimits(S32 max_dim, S32 min_dim);
+    void setMinDimentionsLimits(S32 min_dim);
 	void setTextureSelectedCallback(const texture_selected_callback& cb) { mTextureSelectedCallback = cb; }
 	void setOnFloaterCloseCallback(const floater_close_callback& cb) { mOnFloaterCloseCallback = cb; }
 	void setOnFloaterCommitCallback(const floater_commit_callback& cb) { mOnFloaterCommitCallback = cb; }
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index d10e1ea8c9..f9fe8054a4 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -1285,6 +1285,45 @@ void LLViewerTextureList::decodeAllImages(F32 max_time)
 	<< LL_ENDL;
 }
 
+bool LLViewerTextureList::createUploadFile(LLPointer<LLImageRaw> raw_image,
+                                           const std::string& out_filename,
+                                           const S32 max_image_dimentions,
+                                           const S32 min_image_dimentions)
+{
+    LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+
+    // make a copy, since convertToUploadFile scales raw image
+    LLPointer<LLImageRaw> scale_image = new LLImageRaw(
+        raw_image->getData(),
+        raw_image->getWidth(),
+        raw_image->getHeight(),
+        raw_image->getComponents());
+
+    LLPointer<LLImageJ2C> compressedImage = LLViewerTextureList::convertToUploadFile(scale_image, max_image_dimentions);
+    if (compressedImage->getWidth() < min_image_dimentions || compressedImage->getHeight() < min_image_dimentions)
+    {
+        std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx",
+                                      min_image_dimentions,
+                                      min_image_dimentions,
+                                      compressedImage->getWidth(),
+                                      compressedImage->getHeight());
+        compressedImage->setLastError(reason);
+        return false;
+    }
+    if (compressedImage.isNull())
+    {
+        compressedImage->setLastError("Couldn't convert the image to jpeg2000.");
+        LL_INFOS() << "Couldn't convert to j2c, file : " << out_filename << LL_ENDL;
+        return false;
+    }
+    if (!compressedImage->save(out_filename))
+    {
+        compressedImage->setLastError("Couldn't create the jpeg2000 image for upload.");
+        LL_INFOS() << "Couldn't create output file : " << out_filename << LL_ENDL;
+        return false;
+    }
+    return true;
+}
 
 BOOL LLViewerTextureList::createUploadFile(const std::string& filename,
 										 const std::string& out_filename,
diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h
index 8fc65fc9ce..82dec6b329 100644
--- a/indra/newview/llviewertexturelist.h
+++ b/indra/newview/llviewertexturelist.h
@@ -92,6 +92,10 @@ class LLViewerTextureList
 	friend class LLLocalBitmap;
 	
 public:
+    static bool createUploadFile(LLPointer<LLImageRaw> raw_image,
+                                 const std::string& out_filename,
+                                 const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT,
+                                 const S32 min_image_dimentions = 0);
     static BOOL createUploadFile(const std::string& filename,
                                  const std::string& out_filename,
                                  const U8 codec,
diff --git a/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml
index 771113d989..8081af6673 100644
--- a/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml
+++ b/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml
@@ -96,7 +96,7 @@
      name="over_limit_lbl"
      visible="false"
      top_delta="0">
-        Selected texture is [TEXDIM]. Inventory image must be square, between [MINTEXDIM] and [MAXTEXDIM].
+        Selected texture is [TEXDIM]. Inventory image must be square, no less than [MINTEXDIM].
     </text>
     
 <!--  middle: inventory mode -->
-- 
cgit v1.2.3