123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680 |
- /**
- * @file llpreviewtexture.cpp
- * @brief LLPreviewTexture class implementation
- *
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "llviewerprecompiledheaders.h"
- #include "llpreviewtexture.h"
- #include "llcombobox.h"
- #include "llimagetga.h"
- #include "llinventory.h"
- #include "lllineeditor.h"
- #include "lltrans.h"
- #include "lluictrlfactory.h"
- #include "llagent.h"
- #include "llappviewer.h"
- #include "llfolderview.h"
- #include "lltexturecache.h"
- #include "llviewertexturelist.h"
- #include "llviewerwindow.h"
- constexpr S32 PREVIEW_TEXTURE_MIN_WIDTH = 300;
- constexpr S32 PREVIEW_TEXTURE_MIN_HEIGHT = 120;
- constexpr F32 PREVIEW_TEXTURE_MAX_ASPECT = 200.f;
- constexpr F32 PREVIEW_TEXTURE_MIN_ASPECT = 0.005f;
- constexpr S32 CLIENT_RECT_VPAD = 4;
- constexpr F32 SECONDS_TO_SHOW_FILE_SAVED_MSG = 8.f;
- std::set<LLPreviewTexture*> LLPreviewTexture::sList;
- LLPreviewTexture::LLPreviewTexture(const std::string& name,
- const LLRect& rect,
- const std::string& title,
- const LLUUID& item_uuid,
- const LLUUID& object_id,
- bool show_keep_discard)
- : LLPreview(name, rect, title, item_uuid, object_id, true,
- PREVIEW_TEXTURE_MIN_WIDTH, PREVIEW_TEXTURE_MIN_HEIGHT),
- mShowKeepDiscard(show_keep_discard),
- mCopyToInv(false),
- mIsCopyable(false),
- mLoadingFullImage(false),
- mLastHeight(0),
- mLastWidth(0),
- mAspectRatio(0.f),
- mImage(NULL),
- mImageOldBoostLevel(LLGLTexture::BOOST_NONE)
- {
- sList.insert(this);
- const LLInventoryItem* item = getItem();
- if (item)
- {
- mImageID = item->getAssetUUID();
- const LLPermissions& perm = item->getPermissions();
- U32 mask = PERM_NONE;
- if (perm.getOwner() == gAgentID)
- {
- mask = perm.getMaskBase();
- }
- else if (gAgent.isInGroup(perm.getGroup()))
- {
- mask = perm.getMaskGroup();
- }
- else
- {
- mask = perm.getMaskEveryone();
- }
- if ((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
- {
- mIsCopyable = true;
- }
- }
- init();
- setTitle(title);
- if (!getHost())
- {
- LLRect curRect = getRect();
- translate(rect.mLeft - curRect.mLeft, rect.mTop - curRect.mTop);
- }
- }
- // Note: uses asset_id as a dummy item id.
- LLPreviewTexture::LLPreviewTexture(const std::string& name,
- const LLRect& rect,
- const std::string& title,
- const LLUUID& asset_id,
- bool copy_to_inv)
- : LLPreview(name, rect, title, asset_id, LLUUID::null, true,
- PREVIEW_TEXTURE_MIN_WIDTH, PREVIEW_TEXTURE_MIN_HEIGHT),
- mImageID(asset_id),
- mShowKeepDiscard(false),
- mCopyToInv(copy_to_inv),
- mIsCopyable(true),
- mLoadingFullImage(false),
- mLastHeight(0),
- mLastWidth(0),
- mAspectRatio(0.f),
- mImage(NULL),
- mImageOldBoostLevel(LLGLTexture::BOOST_NONE)
- {
- sList.insert(this);
- init();
- setTitle(title);
- LLRect curRect = getRect();
- translate(curRect.mLeft - rect.mLeft, curRect.mTop - rect.mTop);
- }
- LLPreviewTexture::~LLPreviewTexture()
- {
- LLLoadedCallbackEntry::cleanUpCallbackList(&mCallbackTextureList);
- if (mLoadingFullImage)
- {
- gWindowp->decBusyCount();
- }
- if (mImage.notNull())
- {
- mImage->setBoostLevel(mImageOldBoostLevel);
- mImage = NULL;
- }
- sList.erase(this);
- }
- void LLPreviewTexture::init()
- {
- LLUICtrlFactory::getInstance()->buildFloater(this,
- "floater_preview_texture.xml");
- childSetVisible("desc", !mCopyToInv); // Hide description field for embedded textures
- childSetVisible("desc txt", !mCopyToInv);
- childSetVisible("Copy To Inventory", mCopyToInv);
- childSetVisible("Keep", mShowKeepDiscard);
- childSetVisible("Discard", mShowKeepDiscard);
- if (mCopyToInv)
- {
- childSetAction("Copy To Inventory",LLPreview::onBtnCopyToInv, this);
- }
- else if (mShowKeepDiscard)
- {
- childSetAction("Keep", onKeepBtn, this);
- childSetAction("Discard", onDiscardBtn, this);
- }
- else
- {
- // If the buttons are hidden move stuff down to use the space.
- LLRect keep_rect, old_rect, new_rect;
- S32 diff;
- childGetRect("Keep", keep_rect);
- childGetRect("combo_aspect_ratio", old_rect);
- diff = old_rect.mBottom - keep_rect.mBottom;
- new_rect.setOriginAndSize(old_rect.mLeft, old_rect.mBottom - diff,
- old_rect.getWidth(), old_rect.getHeight());
- childSetRect("combo_aspect_ratio", new_rect);
- childGetRect("aspect_ratio", old_rect);
- new_rect.setOriginAndSize(old_rect.mLeft, old_rect.mBottom - diff,
- old_rect.getWidth(), old_rect.getHeight());
- childSetRect("aspect_ratio", new_rect);
- childGetRect("dimensions", old_rect);
- new_rect.setOriginAndSize(old_rect.mLeft, old_rect.mBottom - diff,
- old_rect.getWidth(), old_rect.getHeight());
- childSetRect("dimensions", new_rect);
- childGetRect("Refresh", old_rect);
- new_rect.setOriginAndSize(old_rect.mLeft, old_rect.mBottom - diff,
- old_rect.getWidth(), old_rect.getHeight());
- childSetRect("Refresh", new_rect);
- }
- if (!mCopyToInv)
- {
- const LLInventoryItem* item = getItem();
- if (item)
- {
- childSetCommitCallback("desc", LLPreview::onText, this);
- childSetText("desc", item->getDescription());
- childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe);
- }
- }
- childSetCommitCallback("combo_aspect_ratio", onAspectRatioCommit, this);
- LLComboBox* combo = getChild<LLComboBox>("combo_aspect_ratio");
- combo->setCurrentByIndex(0);
- childSetAction("Refresh", onRefreshBtn, this);
- }
- void LLPreviewTexture::draw()
- {
- updateDimensions();
- LLPreview::draw();
- if (!isMinimized())
- {
- LLGLSUIDefault gls_ui;
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- const LLRect& border = mClientRect;
- LLRect interior = mClientRect;
- interior.stretch(-PREVIEW_BORDER_WIDTH);
- // ...border
- gl_rect_2d(border, LLColor4(0.f, 0.f, 0.f, 1.f));
- gl_rect_2d_checkerboard(interior);
- if (mImage.notNull())
- {
- // Draw the texture
- glColor3f(1.f, 1.f, 1.f);
- gl_draw_scaled_image(interior.mLeft,
- interior.mBottom,
- interior.getWidth(),
- interior.getHeight(),
- mImage);
- // Pump the texture priority
- F32 pixel_area =
- mLoadingFullImage ? gMaxImageSizeDefault * gMaxImageSizeDefault
- : interior.getWidth() * interior.getHeight();
- mImage->addTextureStats(pixel_area);
- if (pixel_area > 0.f)
- {
- // Boost the previewed image priority to the highest to make it
- // to get loaded first.
- mImage->setAdditionalDecodePriority(1.f);
- }
- // Do not bother decoding more than we can display, unless we are
- // loading the full image.
- if (mLoadingFullImage)
- {
- // Do not use this feature
- mImage->setKnownDrawSize(0, 0);
- }
- else
- {
- S32 int_width = interior.getWidth();
- S32 int_height = interior.getHeight();
- mImage->setKnownDrawSize(int_width, int_height);
- }
- if (mLoadingFullImage)
- {
- static const LLWString load = LLTrans::getWString("receiving");
- LLFontGL::getFontSansSerif()->render(load, 0,
- interior.mLeft + 4,
- interior.mBottom + 4,
- LLColor4::white,
- LLFontGL::LEFT,
- LLFontGL::BOTTOM,
- LLFontGL::DROP_SHADOW);
- F32 data_progress = mImage->getDownloadProgress();
- // Draw the progress bar.
- constexpr S32 BAR_HEIGHT = 12;
- constexpr S32 BAR_LEFT_PAD = 80;
- S32 left = interior.mLeft + 4 + BAR_LEFT_PAD;
- S32 bar_width = getRect().getWidth() - left - RESIZE_HANDLE_WIDTH - 2;
- S32 top = interior.mBottom + 4 + BAR_HEIGHT;
- S32 right = left + bar_width;
- S32 bottom = top - BAR_HEIGHT;
- LLColor4 background_color(0.f, 0.f, 0.f, 0.75f);
- LLColor4 decoded_color(0.f, 1.f, 0.f, 1.0f);
- LLColor4 downloaded_color(0.f, 0.5f, 0.f, 1.0f);
- gl_rect_2d(left, top, right, bottom, background_color);
- if (data_progress > 0.0f)
- {
- // Downloaded bytes
- right = left + llfloor(data_progress * (F32)bar_width);
- if (right > left)
- {
- gl_rect_2d(left, top, right, bottom, downloaded_color);
- }
- }
- }
- else if (!mSavedFileTimer.hasExpired())
- {
- static const LLWString saved =
- LLTrans::getWString("file_saved");
- LLFontGL::getFontSansSerif()->render(saved, 0,
- interior.mLeft + 4,
- interior.mBottom + 4,
- LLColor4::white,
- LLFontGL::LEFT,
- LLFontGL::BOTTOM,
- LLFontGL::DROP_SHADOW);
- }
- }
- }
- }
- //virtual
- bool LLPreviewTexture::canSaveAs() const
- {
- return mIsCopyable && !mLoadingFullImage && mImage.notNull() &&
- !mImage->isMissingAsset();
- }
- //static
- void LLPreviewTexture::saveAsCallback(HBFileSelector::ESaveFilter type,
- std::string& filename, void* user_data)
- {
- LLPreviewTexture* self = (LLPreviewTexture*)user_data;
- if (!self || !sList.count(self) || !self->mImage)
- {
- gNotifications.add("TextureSavingAborted");
- return;
- }
- if (filename.empty()) return;
- // Remember the user-approved/edited file name.
- self->mSaveFileName = filename;
- LLStringUtil::toLower(filename);
- if (filename.rfind(".tga") != filename.length() - 4)
- {
- self->mSaveFileName += ".tga";
- }
- self->mLoadingFullImage = true;
- gWindowp->incBusyCount();
- // Re-fetch the raw image if the old one is removed.
- self->mImage->forceToSaveRawImage(0);
- self->mImage->setLoadedCallback(LLPreviewTexture::onFileLoadedForSave,
- 0, true, false,
- new LLUUID(self->mItemUUID),
- &self->mCallbackTextureList);
- }
- //virtual
- void LLPreviewTexture::saveAs()
- {
- if (mLoadingFullImage) return;
- std::string suggestion;
- const LLViewerInventoryItem* item = getItem();
- if (item)
- {
- suggestion = LLDir::getScrubbedFileName(item->getName());
- }
- HBFileSelector::saveFile(HBFileSelector::FFSAVE_TGA, suggestion,
- saveAsCallback, this);
- }
- //static
- void LLPreviewTexture::onFileLoadedForSave(bool success,
- LLViewerFetchedTexture* src_vi,
- LLImageRaw* src,
- LLImageRaw* aux_src,
- S32 discard_level, bool is_final,
- void* userdata)
- {
- LLUUID* item_uuid = (LLUUID*) userdata;
- LLPreviewTexture* self = NULL;
- preview_map_t::iterator found_it = LLPreview::sInstances.find(*item_uuid);
- if (found_it != LLPreview::sInstances.end())
- {
- self = (LLPreviewTexture*)found_it->second;
- }
- if (is_final || !success)
- {
- delete item_uuid;
- gWindowp->decBusyCount();
- if (self)
- {
- self->mLoadingFullImage = false;
- }
- }
- if (self && is_final && success)
- {
- LLPointer<LLImageTGA> image_tga = new LLImageTGA;
- if (!image_tga->encode(src))
- {
- LLSD args;
- args["FILE"] = self->mSaveFileName;
- gNotifications.add("CannotEncodeFile", args);
- }
- else if (!image_tga->save(self->mSaveFileName))
- {
- LLSD args;
- args["FILE"] = self->mSaveFileName;
- gNotifications.add("CannotWriteFile", args);
- }
- else
- {
- self->mSavedFileTimer.reset();
- self->mSavedFileTimer.setTimerExpirySec(SECONDS_TO_SHOW_FILE_SAVED_MSG);
- }
- self->mSaveFileName.clear();
- }
- if (self && !success)
- {
- gNotifications.add("CannotDownloadFile");
- }
- }
- // It takes a while until we get height and width information. When we receive
- // it, reshape the window accordingly.
- void LLPreviewTexture::updateDimensions()
- {
- if (!mImage || mImage->getFullWidth() == 0 || mImage->getFullHeight() == 0)
- {
- return;
- }
- S32 image_height = llmax(1, mImage->getHeight(0));
- S32 image_width = llmax(1, mImage->getWidth(0));
- // Attempt to make the image 1:1 on screen.
- // If that fails, cut width by half.
- S32 client_width = image_width;
- S32 client_height = image_height;
- constexpr S32 horiz_pad = 2 * (LLPANEL_BORDER_WIDTH + PREVIEW_PAD) +
- PREVIEW_RESIZE_HANDLE_SIZE;
- constexpr S32 vert_pad = PREVIEW_HEADER_SIZE + 2 * CLIENT_RECT_VPAD +
- LLPANEL_BORDER_WIDTH;
- S32 max_client_width = gViewerWindowp->getWindowWidth() - horiz_pad;
- S32 max_client_height = gViewerWindowp->getWindowHeight() - vert_pad;
- if (mAspectRatio > 0.f) client_height = llceil((F32)client_width / mAspectRatio);
- while (client_width > max_client_width ||
- client_height > max_client_height)
- {
- client_width /= 2;
- client_height /= 2;
- }
- S32 view_width = client_width + horiz_pad;
- S32 view_height = client_height + vert_pad;
- // Set text on dimensions display (should be moved out of here and into a
- // callback of some sort)
- childSetTextArg("dimensions", "[WIDTH]",
- llformat("%d", mImage->getFullWidth()));
- childSetTextArg("dimensions", "[HEIGHT]",
- llformat("%d", mImage->getFullHeight()));
- // add space for dimensions and aspect ratio
- S32 info_height = 0;
- LLRect aspect_rect;
- childGetRect("combo_aspect_ratio", aspect_rect);
- S32 aspect_height = aspect_rect.getHeight();
- info_height += aspect_height + CLIENT_RECT_VPAD;
- view_height += info_height;
- S32 button_height = 0;
- if (mShowKeepDiscard || mCopyToInv)
- {
- // add space for buttons
- view_height += gBtnHeight + CLIENT_RECT_VPAD;
- button_height = gBtnHeight + PREVIEW_PAD;
- }
- view_width = llmax(view_width, getMinWidth());
- view_height = llmax(view_height, getMinHeight());
- if (client_height != mLastHeight || client_width != mLastWidth)
- {
- mLastWidth = client_width;
- mLastHeight = client_height;
- S32 old_top = getRect().mTop;
- S32 old_left = getRect().mLeft;
- if (getHost())
- {
- getHost()->growToFit(view_width, view_height);
- }
- else
- {
- reshape(view_width, view_height);
- S32 new_bottom = old_top - getRect().getHeight();
- setOrigin(old_left, new_bottom);
- // Try to keep whole view onscreen, don't allow partial offscreen.
- gFloaterViewp->adjustToFitScreen(this);
- }
- }
- if (!mUserResized)
- {
- // Clamp texture size to fit within actual size of floater after
- // attempting resize
- client_width = llmin(client_width, getRect().getWidth() - horiz_pad);
- client_height = llmin(client_height,
- getRect().getHeight() - PREVIEW_HEADER_SIZE -
- 2 * CLIENT_RECT_VPAD - LLPANEL_BORDER_WIDTH -
- info_height);
- }
- else
- {
- client_width = getRect().getWidth() - horiz_pad;
- if (mAspectRatio > 0.f)
- {
- client_height = ll_roundp(client_width / mAspectRatio);
- }
- else
- {
- client_height = getRect().getHeight() - vert_pad;
- }
- }
- S32 max_height = getRect().getHeight() - PREVIEW_BORDER - button_height -
- CLIENT_RECT_VPAD - info_height - CLIENT_RECT_VPAD -
- PREVIEW_HEADER_SIZE;
- if (mAspectRatio > 0.f)
- {
- max_height = llmax(max_height, 1);
- if (client_height > max_height)
- {
- client_height = max_height;
- client_width = ll_roundp(client_height * mAspectRatio);
- }
- }
- else
- {
- S32 max_width = getRect().getWidth() - horiz_pad;
- client_height = llclamp(client_height, 1, max_height);
- client_width = llclamp(client_width, 1, max_width);
- }
- LLRect window_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
- window_rect.mTop -= PREVIEW_HEADER_SIZE + CLIENT_RECT_VPAD;
- window_rect.mBottom += PREVIEW_BORDER + button_height + CLIENT_RECT_VPAD +
- info_height + CLIENT_RECT_VPAD;
- mClientRect.setLeftTopAndSize(window_rect.getCenterX() - client_width / 2,
- window_rect.mTop,
- client_width, client_height);
- // Hide the aspect ratio label if the window is too narrow. Assumes the
- // label should be to the right of the dimensions.
- LLRect dim_rect, aspect_label_rect;
- childGetRect("aspect_ratio", aspect_label_rect);
- childGetRect("dimensions", dim_rect);
- childSetVisible("aspect_ratio", dim_rect.mRight < aspect_label_rect.mLeft);
- }
- // Return true if everything went fine, false if we somewhat modified the ratio
- // as we bumped on border values
- bool LLPreviewTexture::setAspectRatio(F32 width, F32 height)
- {
- // We do not allow negative width or height. Also, if height is positive
- // but too small, we reset to default. A default 0.f value for mAspectRatio
- // means "unconstrained" in the rest of the code.
- if (width <= 0.f || height <= F_APPROXIMATELY_ZERO)
- {
- mAspectRatio = 0.f;
- return false;
- }
- // Compute and store the ratio
- F32 ratio = width / height;
- mAspectRatio = llclamp(ratio, PREVIEW_TEXTURE_MIN_ASPECT,
- PREVIEW_TEXTURE_MAX_ASPECT);
- // Return false if we clamped the value, true otherwise
- return ratio == mAspectRatio;
- }
- //static
- void LLPreviewTexture::onAspectRatioCommit(LLUICtrl* ctrl, void* userdata)
- {
- LLPreviewTexture* self = (LLPreviewTexture*) userdata;
- std::string ratio(ctrl->getValue().asString());
- std::string::size_type separator(ratio.find_first_of(":/\\"));
- if (separator == std::string::npos)
- {
- // If there is no separator assume we want an unconstrained ratio
- self->setAspectRatio(0.f, 0.f);
- return;
- }
- F32 width, height;
- std::istringstream numerator(ratio.substr(0, separator));
- std::istringstream denominator(ratio.substr(separator + 1));
- numerator >> width;
- denominator >> height;
- // *TODO: we could use the return value to decide to rebuild the width and
- // height string here...
- self->setAspectRatio(width, height);
- }
- //static
- void LLPreviewTexture::onRefreshBtn(void* data)
- {
- LLPreviewTexture* self = (LLPreviewTexture*)data;
- if (self->mImage)
- {
- // Remove the (probably corrupted) cache entry
- gTextureCachep->removeFromCache(self->mImage->getID());
- // Force a reload of the raw image from network
- self->mImage->forceRefetch();
- // Force a reload of the image in GL from the fresh raw image
- self->mImage->destroyGLTexture();
- }
- }
- void LLPreviewTexture::loadAsset()
- {
- mImage =
- LLViewerTextureManager::getFetchedTexture(mImageID, FTT_DEFAULT,
- MIPMAP_YES,
- LLGLTexture::BOOST_NONE,
- LLViewerTexture::LOD_TEXTURE);
- if (mImage.notNull())
- {
- mImageOldBoostLevel = mImage->getBoostLevel();
- mImage->setBoostLevel(LLGLTexture::BOOST_PREVIEW);
- mImage->forceToSaveRawImage(0);
- mAssetStatus = PREVIEW_ASSET_LOADING;
- }
- }
- LLPreview::EAssetStatus LLPreviewTexture::getAssetStatus()
- {
- if (mImage.notNull() &&
- mImage->getFullWidth() * mImage->getFullHeight() > 0)
- {
- mAssetStatus = PREVIEW_ASSET_LOADED;
- }
- return mAssetStatus;
- }
|