2k image resize (#4444)

* Fix spelling error in variable name

* Resize images larger than allowed before upload

* Resize bulk images if they are larger than the allow size

* Fix indentation error caused by Visual Studio

* Fix bulk upload cost calculation
master
Kyler "Félix" Eastridge 2025-07-26 20:18:26 +01:00 committed by GitHub
parent afdcd356db
commit 533390a531
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 248 additions and 40 deletions

View File

@ -32,6 +32,7 @@
#include "llimagetga.h"
#include "llimagejpeg.h"
#include "llimagepng.h"
#include "llimagej2c.h"
#include "llagent.h"
#include "llagentbenefits.h"
@ -43,6 +44,10 @@
#include "llrender.h"
#include "llface.h"
#include "llfocusmgr.h"
#include "llfilesystem.h"
#include "llfloaterperms.h"
#include "llnotificationsutil.h"
#include "llstatusbar.h" // can_afford_transaction()
#include "lltextbox.h"
#include "lltoolmgr.h"
#include "llui.h"
@ -52,6 +57,7 @@
#include "llvoavatar.h"
#include "pipeline.h"
#include "lluictrlfactory.h"
#include "llviewermenufile.h" // upload_new_resource()
#include "llviewershadermgr.h"
#include "llviewertexturelist.h"
#include "llstring.h"
@ -140,7 +146,7 @@ bool LLFloaterImagePreview::postBuild()
}
}
getChild<LLUICtrl>("ok_btn")->setCommitCallback(boost::bind(&LLFloaterNameDesc::onBtnOK, this));
getChild<LLUICtrl>("ok_btn")->setCommitCallback(boost::bind(&LLFloaterImagePreview::onBtnOK, this));
return true;
}
@ -243,6 +249,59 @@ void LLFloaterImagePreview::clearAllPreviewTextures()
}
}
//-----------------------------------------------------------------------------
// onBtnOK()
//-----------------------------------------------------------------------------
void LLFloaterImagePreview::onBtnOK()
{
getChildView("ok_btn")->setEnabled(false); // don't allow inadvertent extra uploads
S32 expected_upload_cost = getExpectedUploadCost();
if (can_afford_transaction(expected_upload_cost))
{
LL_INFOS() << "saving texture: " << mRawImagep->getWidth() << "x" << mRawImagep->getHeight() << LL_ENDL;
// gen a new uuid for this asset
LLTransactionID tid;
tid.generate();
LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
LLPointer<LLImageJ2C> formatted = new LLImageJ2C;
if (formatted->encode(mRawImagep, 0.0f))
{
LLFileSystem fmt_file(new_asset_id, LLAssetType::AT_TEXTURE, LLFileSystem::WRITE);
fmt_file.write(formatted->getData(), formatted->getDataSize());
LLResourceUploadInfo::ptr_t assetUploadInfo(new LLResourceUploadInfo(
tid, LLAssetType::AT_TEXTURE,
getChild<LLUICtrl>("name_form")->getValue().asString(),
getChild<LLUICtrl>("description_form")->getValue().asString(),
0,
LLFolderType::FT_NONE, LLInventoryType::IT_NONE,
LLFloaterPerms::getNextOwnerPerms("Uploads"),
LLFloaterPerms::getGroupPerms("Uploads"),
LLFloaterPerms::getEveryonePerms("Uploads"),
expected_upload_cost
));
upload_new_resource(assetUploadInfo);
}
else
{
LLNotificationsUtil::add("ErrorEncodingImage");
LL_WARNS() << "Error encoding image" << LL_ENDL;
}
}
else
{
LLSD args;
args["COST"] = llformat("%d", expected_upload_cost);
LLNotificationsUtil::add("ErrorCannotAffordUpload", args);
}
closeFloater(false);
}
//-----------------------------------------------------------------------------
// draw()
//-----------------------------------------------------------------------------
@ -364,19 +423,6 @@ bool LLFloaterImagePreview::loadImage(const std::string& src_filename)
return false;
}
S32 max_width = gSavedSettings.getS32("max_texture_dimension_X");
S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y");
if ((image_info.getWidth() > max_width) || (image_info.getHeight() > max_height))
{
LLStringUtil::format_map_t args;
args["WIDTH"] = llformat("%d", max_width);
args["HEIGHT"] = llformat("%d", max_height);
mImageLoadError = LLTrans::getString("texture_load_dimensions_error", args);
return false;
}
// Load the image
LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec);
if (image.isNull())
@ -399,6 +445,46 @@ bool LLFloaterImagePreview::loadImage(const std::string& src_filename)
image->setLastError("Image files with less than 3 or more than 4 components are not supported.");
return false;
}
// Downscale images to fit the max_texture_dimensions_*
S32 max_width = gSavedSettings.getS32("max_texture_dimension_X");
S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y");
S32 orig_width = raw_image->getWidth();
S32 orig_height = raw_image->getHeight();
if (orig_width > max_width || orig_height > max_height)
{
// Calculate scale factors
F32 width_scale = (F32)max_width / (F32)orig_width;
F32 height_scale = (F32)max_height / (F32)orig_height;
F32 scale = llmin(width_scale, height_scale);
// Calculate new dimensions, preserving aspect ratio
S32 new_width = LLImageRaw::contractDimToPowerOfTwo(
llclamp((S32)llroundf(orig_width * scale), 4, max_width)
);
S32 new_height = LLImageRaw::contractDimToPowerOfTwo(
llclamp((S32)llroundf(orig_height * scale), 4, max_height)
);
if (!raw_image->scale(new_width, new_height))
{
LL_WARNS() << "Failed to scale image from "
<< orig_width << "x" << orig_height
<< " to " << new_width << "x" << new_height << LL_ENDL;
return false;
}
// Inform the resident about the resized image
LLSD subs;
subs["[ORIGINAL_WIDTH]"] = orig_width;
subs["[ORIGINAL_HEIGHT]"] = orig_height;
subs["[NEW_WIDTH]"] = new_width;
subs["[NEW_HEIGHT]"] = new_height;
subs["[MAX_WIDTH]"] = max_width;
subs["[MAX_HEIGHT]"] = max_height;
LLNotificationsUtil::add("ImageUploadResized", subs);
}
raw_image->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
mRawImagep = raw_image;

View File

@ -126,6 +126,8 @@ public:
void clearAllPreviewTextures();
void onBtnOK();
protected:
static void onPreviewTypeCommit(LLUICtrl*,void*);
void draw() override;

View File

@ -69,6 +69,7 @@
#include "llviewerassetupload.h"
// linden libraries
#include "llfilesystem.h"
#include "llnotificationsutil.h"
#include "llsdserialize.h"
#include "llsdutil.h"
@ -544,16 +545,9 @@ void do_bulk_upload(std::vector<std::string> filenames, bool allow_2k)
if (LLResourceUploadInfo::findAssetTypeAndCodecOfExtension(ext, asset_type, codec))
{
bool resource_upload = false;
if (asset_type == LLAssetType::AT_TEXTURE && allow_2k)
if (asset_type == LLAssetType::AT_TEXTURE)
{
LLPointer<LLImageFormatted> image_frmted = LLImageFormatted::createFromType(codec);
if (gDirUtilp->fileExists(filename) && image_frmted && image_frmted->load(filename))
{
S32 biased_width = LLImageRaw::biasedDimToPowerOfTwo(image_frmted->getWidth(), LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
S32 biased_height = LLImageRaw::biasedDimToPowerOfTwo(image_frmted->getHeight(), LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(biased_width, biased_height);
resource_upload = true;
}
resource_upload = true;
}
else if (LLAgentBenefitsMgr::current().findUploadCost(asset_type, expected_upload_cost))
{
@ -562,23 +556,115 @@ void do_bulk_upload(std::vector<std::string> filenames, bool allow_2k)
if (resource_upload)
{
LLNewFileResourceUploadInfo* info_p = new LLNewFileResourceUploadInfo(
filename,
asset_name,
asset_name, 0,
LLFolderType::FT_NONE, LLInventoryType::IT_NONE,
LLFloaterPerms::getNextOwnerPerms("Uploads"),
LLFloaterPerms::getGroupPerms("Uploads"),
LLFloaterPerms::getEveryonePerms("Uploads"),
expected_upload_cost);
if (!allow_2k)
if (asset_type == LLAssetType::AT_TEXTURE)
{
info_p->setMaxImageSize(1024);
}
LLResourceUploadInfo::ptr_t uploadInfo(info_p);
std::string exten = gDirUtilp->getExtension(filename);
U32 codec = LLImageBase::getCodecFromExtension(exten);
upload_new_resource(uploadInfo);
// Load the image
LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec);
if (image.isNull())
{
LL_WARNS() << "Failed to create image container for " << filename << LL_ENDL;
continue;
}
if (!image->load(filename))
{
LL_WARNS() << "Failed to load image: " << filename << LL_ENDL;
continue;
}
// Decompress or expand it in a raw image structure
LLPointer<LLImageRaw> raw_image = new LLImageRaw;
if (!image->decode(raw_image, 0.0f))
{
LL_WARNS() << "Failed to decode image: " << filename << LL_ENDL;
continue;
}
// Check the image constraints
if ((image->getComponents() != 3) && (image->getComponents() != 4))
{
LL_WARNS() << "Attempted to upload a texture that has " << image->getComponents()
<< " components, but only 3 (RGB) or 4 (RGBA) are allowed." << LL_ENDL;
continue;
}
// Downscale images to fit the max_texture_dimensions_*, or 1024 if allow_2k is false
S32 max_width = allow_2k ? gSavedSettings.getS32("max_texture_dimension_X") : 1024;
S32 max_height = allow_2k ? gSavedSettings.getS32("max_texture_dimension_Y") : 1024;
S32 orig_width = raw_image->getWidth();
S32 orig_height = raw_image->getHeight();
if (orig_width > max_width || orig_height > max_height)
{
// Calculate scale factors
F32 width_scale = (F32)max_width / (F32)orig_width;
F32 height_scale = (F32)max_height / (F32)orig_height;
F32 scale = llmin(width_scale, height_scale);
// Calculate new dimensions, preserving aspect ratio
S32 new_width = LLImageRaw::contractDimToPowerOfTwo(llclamp((S32)llroundf(orig_width * scale), 4, max_width));
S32 new_height = LLImageRaw::contractDimToPowerOfTwo(llclamp((S32)llroundf(orig_height * scale), 4, max_height));
if (!raw_image->scale(new_width, new_height))
{
LL_WARNS() << "Failed to scale image from " << orig_width << "x" << orig_height << " to " << new_width << "x"
<< new_height << LL_ENDL;
continue;
}
// Inform the resident about the resized image
LLSD subs;
subs["[ORIGINAL_WIDTH]"] = orig_width;
subs["[ORIGINAL_HEIGHT]"] = orig_height;
subs["[NEW_WIDTH]"] = new_width;
subs["[NEW_HEIGHT]"] = new_height;
subs["[MAX_WIDTH]"] = max_width;
subs["[MAX_HEIGHT]"] = max_height;
LLNotificationsUtil::add("ImageUploadResized", subs);
}
raw_image->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
LLTransactionID tid;
tid.generate();
LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
LLPointer<LLImageJ2C> formatted = new LLImageJ2C;
if (formatted->encode(raw_image, 0.0f))
{
LLFileSystem fmt_file(new_asset_id, LLAssetType::AT_TEXTURE, LLFileSystem::WRITE);
fmt_file.write(formatted->getData(), formatted->getDataSize());
LLResourceUploadInfo::ptr_t assetUploadInfo(new LLResourceUploadInfo(
tid, LLAssetType::AT_TEXTURE,
asset_name,
asset_name, 0,
LLFolderType::FT_NONE, LLInventoryType::IT_NONE,
LLFloaterPerms::getNextOwnerPerms("Uploads"),
LLFloaterPerms::getGroupPerms("Uploads"),
LLFloaterPerms::getEveryonePerms("Uploads"),
LLAgentBenefitsMgr::current().getTextureUploadCost(raw_image->getWidth(), raw_image->getHeight())
));
upload_new_resource(assetUploadInfo);
}
}
else
{
LLNewFileResourceUploadInfo* info_p = new LLNewFileResourceUploadInfo(
filename,
asset_name,
asset_name, 0,
LLFolderType::FT_NONE, LLInventoryType::IT_NONE,
LLFloaterPerms::getNextOwnerPerms("Uploads"),
LLFloaterPerms::getGroupPerms("Uploads"),
LLFloaterPerms::getEveryonePerms("Uploads"),
expected_upload_cost);
LLResourceUploadInfo::ptr_t uploadInfo(info_p);
upload_new_resource(uploadInfo);
}
}
}
@ -647,8 +733,31 @@ bool get_bulk_upload_expected_cost(
LLPointer<LLImageFormatted> image_frmted = LLImageFormatted::createFromType(codec);
if (gDirUtilp->fileExists(filename) && image_frmted && image_frmted->load(filename))
{
S32 biased_width = LLImageRaw::biasedDimToPowerOfTwo(image_frmted->getWidth(), LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
S32 biased_height = LLImageRaw::biasedDimToPowerOfTwo(image_frmted->getHeight(), LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
S32 biased_width, biased_height;
S32 max_width = allow_2k ? gSavedSettings.getS32("max_texture_dimension_X") : 1024;
S32 max_height = allow_2k ? gSavedSettings.getS32("max_texture_dimension_Y") : 1024;
S32 orig_width = image_frmted->getWidth();
S32 orig_height = image_frmted->getHeight();
if (orig_width > max_width || orig_height > max_height)
{
// Calculate scale factors
F32 width_scale = (F32)max_width / (F32)orig_width;
F32 height_scale = (F32)max_height / (F32)orig_height;
F32 scale = llmin(width_scale, height_scale);
// Calculate new dimensions, preserving aspect ratio
biased_width = LLImageRaw::contractDimToPowerOfTwo(llclamp((S32)llroundf(orig_width * scale), 4, max_width));
biased_height = LLImageRaw::contractDimToPowerOfTwo(llclamp((S32)llroundf(orig_height * scale), 4, max_height));
}
else
{
biased_width = LLImageRaw::biasedDimToPowerOfTwo(orig_width, LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
biased_height = LLImageRaw::biasedDimToPowerOfTwo(orig_height, LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
}
total_cost += LLAgentBenefitsMgr::current().getTextureUploadCost(biased_width, biased_height);
S32 area = biased_width * biased_height;
if (area >= LLAgentBenefits::MIN_2K_TEXTURE_AREA)

View File

@ -12619,4 +12619,15 @@ are wearing now.
Unable to apply material to the water exclusion surface.
<tag>fail</tag>
</notification>
<notification
icon="notify.tga"
name="ImageUploadResized"
type="alertmodal">
The texture you are uploading has been resized from [ORIGINAL_WIDTH]x[ORIGINAL_HEIGHT] to [NEW_WIDTH]x[NEW_HEIGHT] in order to to fit the maximum size of [MAX_WIDTH]x[MAX_HEIGHT] pixels.
<usetemplate
ignoretext="Image Upload Resized"
name="okignore"
yestext="OK"/>
</notification>
</notifications>