From 74c8b028d42a8c5b080bb861e427f38cedd4ad7c Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Fri, 15 Dec 2023 18:26:14 +0100 Subject: SL-20743 Use LLMutex in LLImageBase for internal data thread-safety --- indra/newview/llsnapshotlivepreview.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'indra/newview/llsnapshotlivepreview.cpp') diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index b7a1832b17..bb7c73b59f 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -766,6 +766,8 @@ void LLSnapshotLivePreview::prepareFreezeFrame() // Get the decoded version of the formatted image getEncodedImage(); + LLImageDataSharedLock lock(mPreviewImageEncoded); + // We need to scale that a bit for display... LLPointer scaled = new LLImageRaw( mPreviewImageEncoded->getData(), @@ -825,13 +827,15 @@ LLPointer LLSnapshotLivePreview::getEncodedImage() { if (!mPreviewImageEncoded) { + LLImageDataSharedLock lock(mPreviewImage); + mPreviewImageEncoded = new LLImageRaw; - + mPreviewImageEncoded->resize( mPreviewImage->getWidth(), mPreviewImage->getHeight(), mPreviewImage->getComponents()); - + if (getSnapshotType() == LLSnapshotModel::SNAPSHOT_TEXTURE) { // We don't store the intermediate formatted image in mFormattedImage in the J2C case @@ -978,6 +982,8 @@ void LLSnapshotLivePreview::getSize(S32& w, S32& h) const void LLSnapshotLivePreview::saveTexture(BOOL outfit_snapshot, std::string name) { + LLImageDataSharedLock lock(mPreviewImage); + LL_DEBUGS("Snapshot") << "saving texture: " << mPreviewImage->getWidth() << "x" << mPreviewImage->getHeight() << LL_ENDL; // gen a new uuid for this asset LLTransactionID tid; -- cgit v1.2.3 From a5261a5fa8fad810ecb5c260d92c3e771822bf58 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Tue, 20 Feb 2024 23:46:23 +0100 Subject: Convert BOOL to bool in llui --- indra/newview/llsnapshotlivepreview.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'indra/newview/llsnapshotlivepreview.cpp') diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index 863cd13678..828572526b 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -56,16 +56,16 @@ #include "llworld.h" #include -const F32 AUTO_SNAPSHOT_TIME_DELAY = 1.f; +constexpr F32 AUTO_SNAPSHOT_TIME_DELAY = 1.f; -F32 SHINE_TIME = 0.5f; -F32 SHINE_WIDTH = 0.6f; -F32 SHINE_OPACITY = 0.3f; -F32 FALL_TIME = 0.6f; -S32 BORDER_WIDTH = 6; -S32 TOP_PANEL_HEIGHT = 30; +constexpr F32 SHINE_TIME = 0.5f; +constexpr F32 SHINE_WIDTH = 0.6f; +constexpr F32 SHINE_OPACITY = 0.3f; +constexpr F32 FALL_TIME = 0.6f; +constexpr S32 BORDER_WIDTH = 6; +constexpr S32 TOP_PANEL_HEIGHT = 30; -const S32 MAX_TEXTURE_SIZE = 512 ; //max upload texture size 512 * 512 +constexpr S32 MAX_TEXTURE_SIZE = 512 ; //max upload texture size 512 * 512 std::set LLSnapshotLivePreview::sList; LLPointer LLSnapshotLivePreview::sSaveLocalImage = NULL; @@ -428,7 +428,7 @@ void LLSnapshotLivePreview::draw() } /*virtual*/ -void LLSnapshotLivePreview::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLSnapshotLivePreview::reshape(S32 width, S32 height, bool called_from_parent) { LLRect old_rect = getRect(); LLView::reshape(width, height, called_from_parent); -- cgit v1.2.3 From 60d3dd98a44230c21803c1606552ee098ed9fa7c Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 21 Feb 2024 21:05:14 +0100 Subject: Convert remaining BOOL to bool --- indra/newview/llsnapshotlivepreview.cpp | 130 ++++++++++++++++---------------- 1 file changed, 65 insertions(+), 65 deletions(-) (limited to 'indra/newview/llsnapshotlivepreview.cpp') diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index 828572526b..63b1dca693 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -79,24 +79,24 @@ LLSnapshotLivePreview::LLSnapshotLivePreview (const LLSnapshotLivePreview::Param mBigThumbnailImage(NULL) , mThumbnailWidth(0), mThumbnailHeight(0), - mThumbnailSubsampled(FALSE), + mThumbnailSubsampled(false), mPreviewImageEncoded(NULL), mFormattedImage(NULL), mShineCountdown(0), mFlashAlpha(0.f), - mNeedsFlash(TRUE), + mNeedsFlash(true), mSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")), mDataSize(0), mSnapshotType(LLSnapshotModel::SNAPSHOT_POSTCARD), mSnapshotFormat(LLSnapshotModel::ESnapshotFormat(gSavedSettings.getS32("SnapshotFormat"))), - mSnapshotUpToDate(FALSE), + mSnapshotUpToDate(false), mCameraPos(LLViewerCamera::getInstance()->getOrigin()), mCameraRot(LLViewerCamera::getInstance()->getQuaternion()), - mSnapshotActive(FALSE), + mSnapshotActive(false), mSnapshotBufferType(LLSnapshotModel::SNAPSHOT_TYPE_COLOR), mFilterName(""), - mAllowRenderUI(TRUE), - mAllowFullScreenPreview(TRUE), + mAllowRenderUI(true), + mAllowFullScreenPreview(true), mViewContainer(NULL) { setSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")); @@ -109,16 +109,16 @@ LLSnapshotLivePreview::LLSnapshotLivePreview (const LLSnapshotLivePreview::Param mWidth[1] = gViewerWindow->getWindowWidthRaw(); mHeight[0] = gViewerWindow->getWindowHeightRaw(); mHeight[1] = gViewerWindow->getWindowHeightRaw(); - mImageScaled[0] = FALSE; - mImageScaled[1] = FALSE; + mImageScaled[0] = false; + mImageScaled[1] = false; mMaxImageSize = MAX_SNAPSHOT_IMAGE_SIZE ; mKeepAspectRatio = gSavedSettings.getBOOL("KeepAspectForSnapshot") ; - mThumbnailUpdateLock = FALSE ; - mThumbnailUpToDate = FALSE ; - mBigThumbnailUpToDate = FALSE ; + mThumbnailUpdateLock = false ; + mThumbnailUpToDate = false ; + mBigThumbnailUpToDate = false ; - mForceUpdateSnapshot = FALSE; + mForceUpdateSnapshot = false; } LLSnapshotLivePreview::~LLSnapshotLivePreview() @@ -153,7 +153,7 @@ F32 LLSnapshotLivePreview::getImageAspect() return (mKeepAspectRatio ? ((F32)getRect().getWidth()) / ((F32)getRect().getHeight()) : ((F32)getWidth()) / ((F32)getHeight())); } -void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail, F32 delay) +void LLSnapshotLivePreview::updateSnapshot(bool new_snapshot, bool new_thumbnail, F32 delay) { LL_DEBUGS("Snapshot") << "updateSnapshot: mSnapshotUpToDate = " << getSnapshotUpToDate() << LL_ENDL; @@ -167,7 +167,7 @@ void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail setSize(mWidth[old_image_index], mHeight[old_image_index]); mFallAnimTimer.start(); } - mSnapshotUpToDate = FALSE; + mSnapshotUpToDate = false; // Update snapshot source rect depending on whether we keep the aspect ratio. LLRect& rect = mImageRect[mCurImageIndex]; @@ -212,8 +212,8 @@ void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail // Update thumbnail if requested. if (new_thumbnail) { - mThumbnailUpToDate = FALSE ; - mBigThumbnailUpToDate = FALSE; + mThumbnailUpToDate = false ; + mBigThumbnailUpToDate = false; } } @@ -241,7 +241,7 @@ void LLSnapshotLivePreview::drawPreviewRect(S32 offset_x, S32 offset_y, LLColor4 glLineWidth(2.0f * line_width) ; LLColor4 color(0.0f, 0.0f, 0.0f, 1.0f) ; gl_rect_2d( mPreviewRect.mLeft + offset_x, mPreviewRect.mTop + offset_y, - mPreviewRect.mRight + offset_x, mPreviewRect.mBottom + offset_y, color, FALSE ) ; + mPreviewRect.mRight + offset_x, mPreviewRect.mBottom + offset_y, color, false ) ; glLineWidth(line_width) ; //draw four alpha rectangles to cover areas outside of the snapshot image @@ -254,20 +254,20 @@ void LLSnapshotLivePreview::drawPreviewRect(S32 offset_x, S32 offset_y, LLColor4 dwr = mThumbnailWidth - mPreviewRect.getWidth() - dwl ; gl_rect_2d(mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mTop + offset_y, - mPreviewRect.mLeft + offset_x, mPreviewRect.mBottom + offset_y, alpha_color, TRUE ) ; + mPreviewRect.mLeft + offset_x, mPreviewRect.mBottom + offset_y, alpha_color, true ) ; gl_rect_2d( mPreviewRect.mRight + offset_x, mPreviewRect.mTop + offset_y, - mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y, alpha_color, TRUE ) ; + mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y, alpha_color, true ) ; } if(mThumbnailHeight > mPreviewRect.getHeight()) { S32 dh = (mThumbnailHeight - mPreviewRect.getHeight()) >> 1 ; gl_rect_2d(mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mBottom + offset_y , - mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y - dh, alpha_color, TRUE ) ; + mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y - dh, alpha_color, true ) ; dh = mThumbnailHeight - mPreviewRect.getHeight() - dh ; gl_rect_2d( mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mTop + offset_y + dh, - mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mTop + offset_y, alpha_color, TRUE ) ; + mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mTop + offset_y, alpha_color, true ) ; } } } @@ -323,7 +323,7 @@ void LLSnapshotLivePreview::draw() } else { - mNeedsFlash = FALSE; + mNeedsFlash = false; } } else @@ -398,7 +398,7 @@ void LLSnapshotLivePreview::draw() gGL.getTexUnit(0)->bind(mViewerImage[old_image_index]); // calculate UV scale // *FIX get this to work with old image - BOOL rescale = !mImageScaled[old_image_index] && mViewerImage[mCurImageIndex].notNull(); + bool rescale = !mImageScaled[old_image_index] && mViewerImage[mCurImageIndex].notNull(); F32 uv_width = rescale ? llmin((F32)mWidth[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getWidth(), 1.f) : 1.f; F32 uv_height = rescale ? llmin((F32)mHeight[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getHeight(), 1.f) : 1.f; gGL.pushMatrix(); @@ -439,18 +439,18 @@ void LLSnapshotLivePreview::reshape(S32 width, S32 height, bool called_from_pare { // We usually resize only on window reshape, so give it a chance to redraw, assign delay updateSnapshot( - TRUE, // new snapshot is needed - FALSE, // thumbnail will be updated either way. + true, // new snapshot is needed + false, // thumbnail will be updated either way. AUTO_SNAPSHOT_TIME_DELAY); // shutter delay. } } } -BOOL LLSnapshotLivePreview::setThumbnailImageSize() +bool LLSnapshotLivePreview::setThumbnailImageSize() { if (getWidth() < 10 || getHeight() < 10) { - return FALSE ; + return false ; } S32 width = (mThumbnailSubsampled ? mPreviewImage->getWidth() : gViewerWindow->getWindowWidthRaw()); S32 height = (mThumbnailSubsampled ? mPreviewImage->getHeight() : gViewerWindow->getWindowHeightRaw()) ; @@ -476,7 +476,7 @@ BOOL LLSnapshotLivePreview::setThumbnailImageSize() if (mThumbnailWidth > width || mThumbnailHeight > height) { - return FALSE ;//if the window is too small, ignore thumbnail updating. + return false ;//if the window is too small, ignore thumbnail updating. } S32 left = 0 , top = mThumbnailHeight, right = mThumbnailWidth, bottom = 0 ; @@ -500,10 +500,10 @@ BOOL LLSnapshotLivePreview::setThumbnailImageSize() } mPreviewRect.set(left - 1, top + 1, right + 1, bottom - 1) ; - return TRUE ; + return true ; } -void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update) +void LLSnapshotLivePreview::generateThumbnailImage(bool force_update) { if(mThumbnailUpdateLock) //in the process of updating { @@ -519,17 +519,17 @@ void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update) } ////lock updating - mThumbnailUpdateLock = TRUE ; + mThumbnailUpdateLock = true ; if(!setThumbnailImageSize()) { - mThumbnailUpdateLock = FALSE ; - mThumbnailUpToDate = TRUE ; + mThumbnailUpdateLock = false ; + mThumbnailUpToDate = true ; return ; } // Invalidate the big thumbnail when we regenerate the small one - mBigThumbnailUpToDate = FALSE; + mBigThumbnailUpToDate = false; if(mThumbnailImage) { @@ -558,7 +558,7 @@ void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update) mThumbnailWidth, mThumbnailHeight, mAllowRenderUI && gSavedSettings.getBOOL("RenderUIInSnapshot"), gSavedSettings.getBOOL("RenderHUDInSnapshot"), - FALSE, + false, gSavedSettings.getBOOL("RenderSnapshotNoPost"), mSnapshotBufferType) ) { @@ -585,12 +585,12 @@ void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update) } // Scale to a power of 2 so it can be mapped to a texture raw->expandToPowerOfTwo(); - mThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE); - mThumbnailUpToDate = TRUE ; + mThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), false); + mThumbnailUpToDate = true ; } //unlock updating - mThumbnailUpdateLock = FALSE ; + mThumbnailUpdateLock = false ; } LLViewerTexture* LLSnapshotLivePreview::getBigThumbnailImage() @@ -633,38 +633,38 @@ LLViewerTexture* LLSnapshotLivePreview::getBigThumbnailImage() } // Scale to a power of 2 so it can be mapped to a texture raw->expandToPowerOfTwo(); - mBigThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE); - mBigThumbnailUpToDate = TRUE ; + mBigThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), false); + mBigThumbnailUpToDate = true ; } return mBigThumbnailImage ; } // Called often. Checks whether it's time to grab a new snapshot and if so, does it. -// Returns TRUE if new snapshot generated, FALSE otherwise. +// Returns true if new snapshot generated, false otherwise. //static -BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) +bool LLSnapshotLivePreview::onIdle( void* snapshot_preview ) { LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)snapshot_preview; if (previewp->getWidth() == 0 || previewp->getHeight() == 0) { LL_WARNS("Snapshot") << "Incorrect dimensions: " << previewp->getWidth() << "x" << previewp->getHeight() << LL_ENDL; - return FALSE; + return false; } if (previewp->mSnapshotDelayTimer.getStarted()) // Wait for a snapshot delay timer { if (!previewp->mSnapshotDelayTimer.hasExpired()) { - return FALSE; + return false; } previewp->mSnapshotDelayTimer.stop(); } if (LLToolCamera::getInstance()->hasMouseCapture()) // Hide full-screen preview while camming, either don't take snapshots while ALT-zoom active { - previewp->setVisible(FALSE); - return FALSE; + previewp->setVisible(false); + return false; } // If we're in freeze-frame and/or auto update mode and camera has moved, update snapshot. @@ -678,18 +678,18 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) previewp->mCameraPos = new_camera_pos; previewp->mCameraRot = new_camera_rot; // request a new snapshot whenever the camera moves, with a time delay - BOOL new_snapshot = gSavedSettings.getBOOL("AutoSnapshot") || previewp->mForceUpdateSnapshot; + bool new_snapshot = gSavedSettings.getBOOL("AutoSnapshot") || previewp->mForceUpdateSnapshot; LL_DEBUGS("Snapshot") << "camera moved, updating thumbnail" << LL_ENDL; previewp->updateSnapshot( new_snapshot, // whether a new snapshot is needed or merely invalidate the existing one - FALSE, // or if 1st arg is false, whether to produce a new thumbnail image. + false, // or if 1st arg is false, whether to produce a new thumbnail image. new_snapshot ? AUTO_SNAPSHOT_TIME_DELAY : 0.f); // shutter delay if 1st arg is true. - previewp->mForceUpdateSnapshot = FALSE; + previewp->mForceUpdateSnapshot = false; } if (previewp->getSnapshotUpToDate() && previewp->getThumbnailUpToDate()) { - return FALSE; + return false; } // time to produce a snapshot @@ -701,13 +701,13 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) previewp->mPreviewImage = new LLImageRaw; } - previewp->mSnapshotActive = TRUE; + previewp->mSnapshotActive = true; - previewp->setVisible(FALSE); - previewp->setEnabled(FALSE); + previewp->setVisible(false); + previewp->setEnabled(false); previewp->getWindow()->incBusyCount(); - previewp->setImageScaled(FALSE); + previewp->setImageScaled(false); // grab the raw image if (gViewerWindow->rawSnapshot( @@ -718,7 +718,7 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) previewp->getSnapshotType() == LLSnapshotModel::SNAPSHOT_TEXTURE, previewp->mAllowRenderUI && gSavedSettings.getBOOL("RenderUIInSnapshot"), gSavedSettings.getBOOL("RenderHUDInSnapshot"), - FALSE, + false, gSavedSettings.getBOOL("RenderSnapshotNoPost"), previewp->mSnapshotBufferType, previewp->getMaxImageSize())) @@ -737,15 +737,15 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) } // The snapshot is updated now... - previewp->mSnapshotUpToDate = TRUE; + previewp->mSnapshotUpToDate = true; // We need to update the thumbnail though previewp->setThumbnailImageSize(); - previewp->generateThumbnailImage(TRUE) ; + previewp->generateThumbnailImage(true) ; } previewp->getWindow()->decBusyCount(); previewp->setVisible(gSavedSettings.getBOOL("UseFreezeFrame") && previewp->mAllowFullScreenPreview); // only show fullscreen preview when in freeze frame mode - previewp->mSnapshotActive = FALSE; + previewp->mSnapshotActive = false; LL_DEBUGS("Snapshot") << "done creating snapshot" << LL_ENDL; } @@ -760,7 +760,7 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) previewp->mViewContainer->notify(LLSD().with("snapshot-updated", true)); } - return TRUE; + return true; } void LLSnapshotLivePreview::prepareFreezeFrame() @@ -784,15 +784,15 @@ void LLSnapshotLivePreview::prepareFreezeFrame() { // go ahead and shrink image to appropriate power of 2 for display scaled->biasedScaleToPowerOfTwo(1024); - setImageScaled(TRUE); + setImageScaled(true); } else { // expand image but keep original image data intact - scaled->expandToPowerOfTwo(1024, FALSE); + scaled->expandToPowerOfTwo(1024, false); } - mViewerImage[mCurImageIndex] = LLViewerTextureManager::getLocalTexture(scaled.get(), FALSE); + mViewerImage[mCurImageIndex] = LLViewerTextureManager::getLocalTexture(scaled.get(), false); LLPointer curr_preview_image = mViewerImage[mCurImageIndex]; gGL.getTexUnit(0)->bind(curr_preview_image); curr_preview_image->setFilteringOption(getSnapshotType() == LLSnapshotModel::SNAPSHOT_TEXTURE ? LLTexUnit::TFO_ANISOTROPIC : LLTexUnit::TFO_POINT); @@ -851,7 +851,7 @@ LLPointer LLSnapshotLivePreview::getEncodedImage() mPreviewImage->getComponents()); // Scale it as required by J2C scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE); - setImageScaled(TRUE); + setImageScaled(true); // Compress to J2C if (formatted->encode(scaled, 0.f)) { @@ -982,7 +982,7 @@ void LLSnapshotLivePreview::getSize(S32& w, S32& h) const h = getHeight(); } -void LLSnapshotLivePreview::saveTexture(BOOL outfit_snapshot, std::string name) +void LLSnapshotLivePreview::saveTexture(bool outfit_snapshot, std::string name) { LLImageDataSharedLock lock(mPreviewImage); @@ -1065,5 +1065,5 @@ void LLSnapshotLivePreview::saveLocal(LLPointer image, const s { sSaveLocalImage = image; - gViewerWindow->saveImageNumbered(sSaveLocalImage, FALSE, success_cb, failure_cb); + gViewerWindow->saveImageNumbered(sSaveLocalImage, false, success_cb, failure_cb); } -- cgit v1.2.3 From e2e37cced861b98de8c1a7c9c0d3a50d2d90e433 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 22 May 2024 21:25:21 +0200 Subject: Fix line endlings --- indra/newview/llsnapshotlivepreview.cpp | 2138 +++++++++++++++---------------- 1 file changed, 1069 insertions(+), 1069 deletions(-) (limited to 'indra/newview/llsnapshotlivepreview.cpp') diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index f8e3088324..787dd3b667 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -1,1069 +1,1069 @@ -/** -* @file llsnapshotlivepreview.cpp -* @brief Implementation of llsnapshotlivepreview -* @author Gilbert@lindenlab.com -* -* $LicenseInfo:firstyear=2013&license=viewerlgpl$ -* Second Life Viewer Source Code -* Copyright (C) 2014, Linden Research, Inc. -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License as published by the Free Software Foundation; -* version 2.1 of the License only. -* -* This library is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this library; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -* -* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -* $/LicenseInfo$ -*/ - -#include "llviewerprecompiledheaders.h" - -#include "llagent.h" -#include "llagentbenefits.h" -#include "llagentcamera.h" -#include "llagentui.h" -#include "llfilesystem.h" -#include "llcombobox.h" -#include "llfloaterperms.h" -#include "llfloaterreg.h" -#include "llimagefilter.h" -#include "llimagefiltersmanager.h" -#include "llimagebmp.h" -#include "llimagej2c.h" -#include "llimagejpeg.h" -#include "llimagepng.h" -#include "lllandmarkactions.h" -#include "lllocalcliprect.h" -#include "llresmgr.h" -#include "llnotificationsutil.h" -#include "llslurl.h" -#include "llsnapshotlivepreview.h" -#include "lltoolfocus.h" -#include "llviewercontrol.h" -#include "llviewermenufile.h" // upload_new_resource() -#include "llviewerstats.h" -#include "llviewertexturelist.h" -#include "llwindow.h" -#include "llworld.h" -#include - -constexpr F32 AUTO_SNAPSHOT_TIME_DELAY = 1.f; - -constexpr F32 SHINE_TIME = 0.5f; -constexpr F32 SHINE_WIDTH = 0.6f; -constexpr F32 SHINE_OPACITY = 0.3f; -constexpr F32 FALL_TIME = 0.6f; -constexpr S32 BORDER_WIDTH = 6; -constexpr S32 TOP_PANEL_HEIGHT = 30; - -constexpr S32 MAX_TEXTURE_SIZE = 512 ; //max upload texture size 512 * 512 - -std::set LLSnapshotLivePreview::sList; -LLPointer LLSnapshotLivePreview::sSaveLocalImage = NULL; - -LLSnapshotLivePreview::LLSnapshotLivePreview (const LLSnapshotLivePreview::Params& p) - : LLView(p), - mColor(1.f, 0.f, 0.f, 0.5f), - mCurImageIndex(0), - mPreviewImage(NULL), - mThumbnailImage(NULL) , - mBigThumbnailImage(NULL) , - mThumbnailWidth(0), - mThumbnailHeight(0), - mThumbnailSubsampled(false), - mPreviewImageEncoded(NULL), - mFormattedImage(NULL), - mShineCountdown(0), - mFlashAlpha(0.f), - mNeedsFlash(true), - mSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")), - mDataSize(0), - mSnapshotType(LLSnapshotModel::SNAPSHOT_POSTCARD), - mSnapshotFormat(LLSnapshotModel::ESnapshotFormat(gSavedSettings.getS32("SnapshotFormat"))), - mSnapshotUpToDate(false), - mCameraPos(LLViewerCamera::getInstance()->getOrigin()), - mCameraRot(LLViewerCamera::getInstance()->getQuaternion()), - mSnapshotActive(false), - mSnapshotBufferType(LLSnapshotModel::SNAPSHOT_TYPE_COLOR), - mFilterName(""), - mAllowRenderUI(true), - mAllowFullScreenPreview(true), - mViewContainer(NULL) -{ - setSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")); - mSnapshotDelayTimer.setTimerExpirySec(0.0f); - mSnapshotDelayTimer.start(); - // gIdleCallbacks.addFunction( &LLSnapshotLivePreview::onIdle, (void*)this ); - sList.insert(this); - setFollowsAll(); - mWidth[0] = gViewerWindow->getWindowWidthRaw(); - mWidth[1] = gViewerWindow->getWindowWidthRaw(); - mHeight[0] = gViewerWindow->getWindowHeightRaw(); - mHeight[1] = gViewerWindow->getWindowHeightRaw(); - mImageScaled[0] = false; - mImageScaled[1] = false; - - mMaxImageSize = MAX_SNAPSHOT_IMAGE_SIZE ; - mKeepAspectRatio = gSavedSettings.getBOOL("KeepAspectForSnapshot") ; - mThumbnailUpdateLock = false ; - mThumbnailUpToDate = false ; - mBigThumbnailUpToDate = false ; - - mForceUpdateSnapshot = false; -} - -LLSnapshotLivePreview::~LLSnapshotLivePreview() -{ - // delete images - mPreviewImage = NULL; - mPreviewImageEncoded = NULL; - mFormattedImage = NULL; - - // gIdleCallbacks.deleteFunction( &LLSnapshotLivePreview::onIdle, (void*)this ); - sList.erase(this); - sSaveLocalImage = NULL; -} - -void LLSnapshotLivePreview::setMaxImageSize(S32 size) -{ - mMaxImageSize = llmin(size,(S32)(MAX_SNAPSHOT_IMAGE_SIZE)); -} - -LLViewerTexture* LLSnapshotLivePreview::getCurrentImage() -{ - return mViewerImage[mCurImageIndex]; -} - -F32 LLSnapshotLivePreview::getImageAspect() -{ - if (!getCurrentImage()) - { - return 0.f; - } - // mKeepAspectRatio) == gSavedSettings.getBOOL("KeepAspectForSnapshot")) - return (mKeepAspectRatio ? ((F32)getRect().getWidth()) / ((F32)getRect().getHeight()) : ((F32)getWidth()) / ((F32)getHeight())); -} - -void LLSnapshotLivePreview::updateSnapshot(bool new_snapshot, bool new_thumbnail, F32 delay) -{ - LL_DEBUGS("Snapshot") << "updateSnapshot: mSnapshotUpToDate = " << getSnapshotUpToDate() << LL_ENDL; - - // Update snapshot if requested. - if (new_snapshot) - { - if (getSnapshotUpToDate()) - { - S32 old_image_index = mCurImageIndex; - mCurImageIndex = (mCurImageIndex + 1) % 2; - setSize(mWidth[old_image_index], mHeight[old_image_index]); - mFallAnimTimer.start(); - } - mSnapshotUpToDate = false; - - // Update snapshot source rect depending on whether we keep the aspect ratio. - LLRect& rect = mImageRect[mCurImageIndex]; - rect.set(0, getRect().getHeight(), getRect().getWidth(), 0); - - F32 image_aspect_ratio = ((F32)getWidth()) / ((F32)getHeight()); - F32 window_aspect_ratio = ((F32)getRect().getWidth()) / ((F32)getRect().getHeight()); - - if (mKeepAspectRatio)//gSavedSettings.getBOOL("KeepAspectForSnapshot")) - { - if (image_aspect_ratio > window_aspect_ratio) - { - // trim off top and bottom - S32 new_height = ll_round((F32)getRect().getWidth() / image_aspect_ratio); - rect.mBottom += (getRect().getHeight() - new_height) / 2; - rect.mTop -= (getRect().getHeight() - new_height) / 2; - } - else if (image_aspect_ratio < window_aspect_ratio) - { - // trim off left and right - S32 new_width = ll_round((F32)getRect().getHeight() * image_aspect_ratio); - rect.mLeft += (getRect().getWidth() - new_width) / 2; - rect.mRight -= (getRect().getWidth() - new_width) / 2; - } - } - - // Stop shining animation. - mShineAnimTimer.stop(); - mSnapshotDelayTimer.start(); - mSnapshotDelayTimer.resetWithExpiry(delay); - - - mPosTakenGlobal = gAgentCamera.getCameraPositionGlobal(); - - // Tell the floater container that the snapshot is in the process of updating itself - if (mViewContainer) - { - mViewContainer->notify(LLSD().with("snapshot-updating", true)); - } - } - - // Update thumbnail if requested. - if (new_thumbnail) - { - mThumbnailUpToDate = false ; - mBigThumbnailUpToDate = false; - } -} - -// Return true if the quality has been changed, false otherwise -bool LLSnapshotLivePreview::setSnapshotQuality(S32 quality, bool set_by_user) -{ - llclamp(quality, 0, 100); - if (quality != mSnapshotQuality) - { - mSnapshotQuality = quality; - if (set_by_user) - { - gSavedSettings.setS32("SnapshotQuality", quality); - } - mFormattedImage = NULL; // Invalidate the already formatted image if any - return true; - } - return false; -} - -void LLSnapshotLivePreview::drawPreviewRect(S32 offset_x, S32 offset_y, LLColor4 alpha_color) -{ - F32 line_width ; - glGetFloatv(GL_LINE_WIDTH, &line_width) ; - glLineWidth(2.0f * line_width) ; - LLColor4 color(0.0f, 0.0f, 0.0f, 1.0f) ; - gl_rect_2d( mPreviewRect.mLeft + offset_x, mPreviewRect.mTop + offset_y, - mPreviewRect.mRight + offset_x, mPreviewRect.mBottom + offset_y, color, false ) ; - glLineWidth(line_width) ; - - //draw four alpha rectangles to cover areas outside of the snapshot image - if(!mKeepAspectRatio) - { - S32 dwl = 0, dwr = 0 ; - if(mThumbnailWidth > mPreviewRect.getWidth()) - { - dwl = (mThumbnailWidth - mPreviewRect.getWidth()) >> 1 ; - dwr = mThumbnailWidth - mPreviewRect.getWidth() - dwl ; - - gl_rect_2d(mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mTop + offset_y, - mPreviewRect.mLeft + offset_x, mPreviewRect.mBottom + offset_y, alpha_color, true ) ; - gl_rect_2d( mPreviewRect.mRight + offset_x, mPreviewRect.mTop + offset_y, - mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y, alpha_color, true ) ; - } - - if(mThumbnailHeight > mPreviewRect.getHeight()) - { - S32 dh = (mThumbnailHeight - mPreviewRect.getHeight()) >> 1 ; - gl_rect_2d(mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mBottom + offset_y , - mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y - dh, alpha_color, true ) ; - - dh = mThumbnailHeight - mPreviewRect.getHeight() - dh ; - gl_rect_2d( mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mTop + offset_y + dh, - mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mTop + offset_y, alpha_color, true ) ; - } - } -} - -//called when the frame is frozen. -void LLSnapshotLivePreview::draw() -{ - if (getCurrentImage() && - mPreviewImageEncoded.notNull() && - getSnapshotUpToDate()) - { - LLColor4 bg_color(0.f, 0.f, 0.3f, 0.4f); - gl_rect_2d(getRect(), bg_color); - const LLRect& rect = getImageRect(); - LLRect shadow_rect = rect; - shadow_rect.stretch(BORDER_WIDTH); - gl_drop_shadow(shadow_rect.mLeft, shadow_rect.mTop, shadow_rect.mRight, shadow_rect.mBottom, LLColor4(0.f, 0.f, 0.f, mNeedsFlash ? 0.f :0.5f), 10); - - LLColor4 image_color(1.f, 1.f, 1.f, 1.f); - gGL.color4fv(image_color.mV); - gGL.getTexUnit(0)->bind(getCurrentImage()); - // calculate UV scale - F32 uv_width = isImageScaled() ? 1.f : llmin((F32)getWidth() / (F32)getCurrentImage()->getWidth(), 1.f); - F32 uv_height = isImageScaled() ? 1.f : llmin((F32)getHeight() / (F32)getCurrentImage()->getHeight(), 1.f); - gGL.pushMatrix(); - { - gGL.translatef((F32)rect.mLeft, (F32)rect.mBottom + TOP_PANEL_HEIGHT, 0.f); - gGL.begin(LLRender::QUADS); - { - gGL.texCoord2f(uv_width, uv_height); - gGL.vertex2i(rect.getWidth(), rect.getHeight() ); - - gGL.texCoord2f(0.f, uv_height); - gGL.vertex2i(0, rect.getHeight() ); - - gGL.texCoord2f(0.f, 0.f); - gGL.vertex2i(0, 0); - - gGL.texCoord2f(uv_width, 0.f); - gGL.vertex2i(rect.getWidth(), 0); - } - gGL.end(); - } - gGL.popMatrix(); - - gGL.color4f(1.f, 1.f, 1.f, mFlashAlpha); - gl_rect_2d(getRect()); - if (mNeedsFlash) - { - if (mFlashAlpha < 1.f) - { - mFlashAlpha = lerp(mFlashAlpha, 1.f, LLCriticalDamp::getInterpolant(0.02f)); - } - else - { - mNeedsFlash = false; - } - } - else - { - mFlashAlpha = lerp(mFlashAlpha, 0.f, LLCriticalDamp::getInterpolant(0.15f)); - } - - // Draw shining animation if appropriate. - if (mShineCountdown > 0) - { - mShineCountdown--; - if (mShineCountdown == 0) - { - mShineAnimTimer.start(); - } - } - else if (mShineAnimTimer.getStarted()) - { - LL_DEBUGS("Snapshot") << "Drawing shining animation" << LL_ENDL; - F32 shine_interp = llmin(1.f, mShineAnimTimer.getElapsedTimeF32() / SHINE_TIME); - - // draw "shine" effect - LLRect local_rect(0, getRect().getHeight() + TOP_PANEL_HEIGHT, getRect().getWidth(), 0); - LLLocalClipRect clip(local_rect); - { - // draw diagonal stripe with gradient that passes over screen - S32 x1 = gViewerWindow->getWindowWidthScaled() * ll_round((clamp_rescale(shine_interp, 0.f, 1.f, -1.f - SHINE_WIDTH, 1.f))); - S32 x2 = x1 + ll_round(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH); - S32 x3 = x2 + ll_round(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH); - S32 y1 = 0; - S32 y2 = gViewerWindow->getWindowHeightScaled() + TOP_PANEL_HEIGHT; - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.begin(LLRender::QUADS); - { - gGL.color4f(1.f, 1.f, 1.f, 0.f); - gGL.vertex2i(x1, y1); - gGL.vertex2i(x1 + gViewerWindow->getWindowWidthScaled(), y2); - gGL.color4f(1.f, 1.f, 1.f, SHINE_OPACITY); - gGL.vertex2i(x2 + gViewerWindow->getWindowWidthScaled(), y2); - gGL.vertex2i(x2, y1); - - gGL.color4f(1.f, 1.f, 1.f, SHINE_OPACITY); - gGL.vertex2i(x2, y1); - gGL.vertex2i(x2 + gViewerWindow->getWindowWidthScaled(), y2); - gGL.color4f(1.f, 1.f, 1.f, 0.f); - gGL.vertex2i(x3 + gViewerWindow->getWindowWidthScaled(), y2); - gGL.vertex2i(x3, y1); - } - gGL.end(); - } - - // if we're at the end of the animation, stop - if (shine_interp >= 1.f) - { - mShineAnimTimer.stop(); - } - } - } - - // draw old image dropping away - if (mFallAnimTimer.getStarted()) - { - S32 old_image_index = (mCurImageIndex + 1) % 2; - if (mViewerImage[old_image_index].notNull() && mFallAnimTimer.getElapsedTimeF32() < FALL_TIME) - { - LL_DEBUGS("Snapshot") << "Drawing fall animation" << LL_ENDL; - F32 fall_interp = mFallAnimTimer.getElapsedTimeF32() / FALL_TIME; - F32 alpha = clamp_rescale(fall_interp, 0.f, 1.f, 0.8f, 0.4f); - LLColor4 image_color(1.f, 1.f, 1.f, alpha); - gGL.color4fv(image_color.mV); - gGL.getTexUnit(0)->bind(mViewerImage[old_image_index]); - // calculate UV scale - // *FIX get this to work with old image - bool rescale = !mImageScaled[old_image_index] && mViewerImage[mCurImageIndex].notNull(); - F32 uv_width = rescale ? llmin((F32)mWidth[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getWidth(), 1.f) : 1.f; - F32 uv_height = rescale ? llmin((F32)mHeight[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getHeight(), 1.f) : 1.f; - gGL.pushMatrix(); - { - LLRect& rect = mImageRect[old_image_index]; - gGL.translatef((F32)rect.mLeft, (F32)rect.mBottom - ll_round(getRect().getHeight() * 2.f * (fall_interp * fall_interp)), 0.f); - gGL.rotatef(-45.f * fall_interp, 0.f, 0.f, 1.f); - gGL.begin(LLRender::QUADS); - { - gGL.texCoord2f(uv_width, uv_height); - gGL.vertex2i(rect.getWidth(), rect.getHeight() ); - - gGL.texCoord2f(0.f, uv_height); - gGL.vertex2i(0, rect.getHeight() ); - - gGL.texCoord2f(0.f, 0.f); - gGL.vertex2i(0, 0); - - gGL.texCoord2f(uv_width, 0.f); - gGL.vertex2i(rect.getWidth(), 0); - } - gGL.end(); - } - gGL.popMatrix(); - } - } -} - -/*virtual*/ -void LLSnapshotLivePreview::reshape(S32 width, S32 height, bool called_from_parent) -{ - LLRect old_rect = getRect(); - LLView::reshape(width, height, called_from_parent); - if (old_rect.getWidth() != width || old_rect.getHeight() != height) - { - LL_DEBUGS("Window", "Snapshot") << "window reshaped, updating thumbnail" << LL_ENDL; - if (mViewContainer && mViewContainer->isInVisibleChain()) - { - // We usually resize only on window reshape, so give it a chance to redraw, assign delay - updateSnapshot( - true, // new snapshot is needed - false, // thumbnail will be updated either way. - AUTO_SNAPSHOT_TIME_DELAY); // shutter delay. - } - } -} - -bool LLSnapshotLivePreview::setThumbnailImageSize() -{ - if (getWidth() < 10 || getHeight() < 10) - { - return false ; - } - S32 width = (mThumbnailSubsampled ? mPreviewImage->getWidth() : gViewerWindow->getWindowWidthRaw()); - S32 height = (mThumbnailSubsampled ? mPreviewImage->getHeight() : gViewerWindow->getWindowHeightRaw()) ; - - F32 aspect_ratio = ((F32)width) / ((F32)height); - - // UI size for thumbnail - S32 max_width = mThumbnailPlaceholderRect.getWidth(); - S32 max_height = mThumbnailPlaceholderRect.getHeight(); - - if (aspect_ratio > (F32)max_width / (F32)max_height) - { - // image too wide, shrink to width - mThumbnailWidth = max_width; - mThumbnailHeight = ll_round((F32)max_width / aspect_ratio); - } - else - { - // image too tall, shrink to height - mThumbnailHeight = max_height; - mThumbnailWidth = ll_round((F32)max_height * aspect_ratio); - } - - if (mThumbnailWidth > width || mThumbnailHeight > height) - { - return false ;//if the window is too small, ignore thumbnail updating. - } - - S32 left = 0 , top = mThumbnailHeight, right = mThumbnailWidth, bottom = 0 ; - if (!mKeepAspectRatio) - { - F32 ratio_x = (F32)getWidth() / width ; - F32 ratio_y = (F32)getHeight() / height ; - - if (ratio_x > ratio_y) - { - top = (S32)(top * ratio_y / ratio_x) ; - } - else - { - right = (S32)(right * ratio_x / ratio_y) ; - } - left = (S32)((mThumbnailWidth - right) * 0.5f) ; - bottom = (S32)((mThumbnailHeight - top) * 0.5f) ; - top += bottom ; - right += left ; - } - mPreviewRect.set(left - 1, top + 1, right + 1, bottom - 1) ; - - return true ; -} - -void LLSnapshotLivePreview::generateThumbnailImage(bool force_update) -{ - if(mThumbnailUpdateLock) //in the process of updating - { - return ; - } - if(getThumbnailUpToDate() && !force_update)//already updated - { - return ; - } - if(getWidth() < 10 || getHeight() < 10) - { - return ; - } - - ////lock updating - mThumbnailUpdateLock = true ; - - if(!setThumbnailImageSize()) - { - mThumbnailUpdateLock = false ; - mThumbnailUpToDate = true ; - return ; - } - - // Invalidate the big thumbnail when we regenerate the small one - mBigThumbnailUpToDate = false; - - if(mThumbnailImage) - { - resetThumbnailImage() ; - } - - LLPointer raw = new LLImageRaw; - - if (mThumbnailSubsampled) - { - // The thumbnail is be a subsampled version of the preview (used in SL Share previews, i.e. Flickr, Twitter) - raw->resize( mPreviewImage->getWidth(), - mPreviewImage->getHeight(), - mPreviewImage->getComponents()); - raw->copy(mPreviewImage); - // Scale to the thumbnail size - if (!raw->scale(mThumbnailWidth, mThumbnailHeight)) - { - raw = NULL ; - } - } - else - { - // The thumbnail is a screen view with screen grab positioning preview - if(!gViewerWindow->thumbnailSnapshot(raw, - mThumbnailWidth, mThumbnailHeight, - mAllowRenderUI && gSavedSettings.getBOOL("RenderUIInSnapshot"), - gSavedSettings.getBOOL("RenderHUDInSnapshot"), - false, - gSavedSettings.getBOOL("RenderSnapshotNoPost"), - mSnapshotBufferType) ) - { - raw = NULL ; - } - } - - if (raw) - { - // Filter the thumbnail - // Note: filtering needs to be done *before* the scaling to power of 2 or the effect is distorted - if (getFilter() != "") - { - std::string filter_path = LLImageFiltersManager::getInstance()->getFilterPath(getFilter()); - if (filter_path != "") - { - LLImageFilter filter(filter_path); - filter.executeFilter(raw); - } - else - { - LL_WARNS("Snapshot") << "Couldn't find a path to the following filter : " << getFilter() << LL_ENDL; - } - } - // Scale to a power of 2 so it can be mapped to a texture - raw->expandToPowerOfTwo(); - mThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), false); - mThumbnailUpToDate = true ; - } - - //unlock updating - mThumbnailUpdateLock = false ; -} - -LLViewerTexture* LLSnapshotLivePreview::getBigThumbnailImage() -{ - if (mThumbnailUpdateLock) //in the process of updating - { - return NULL; - } - if (mBigThumbnailUpToDate && mBigThumbnailImage)//already updated - { - return mBigThumbnailImage; - } - - LLPointer raw = new LLImageRaw; - - if (raw) - { - // The big thumbnail is a new filtered version of the preview (used in SL Share previews, i.e. Flickr, Twitter) - mBigThumbnailWidth = mPreviewImage->getWidth(); - mBigThumbnailHeight = mPreviewImage->getHeight(); - raw->resize( mBigThumbnailWidth, - mBigThumbnailHeight, - mPreviewImage->getComponents()); - raw->copy(mPreviewImage); - - // Filter - // Note: filtering needs to be done *before* the scaling to power of 2 or the effect is distorted - if (getFilter() != "") - { - std::string filter_path = LLImageFiltersManager::getInstance()->getFilterPath(getFilter()); - if (filter_path != "") - { - LLImageFilter filter(filter_path); - filter.executeFilter(raw); - } - else - { - LL_WARNS("Snapshot") << "Couldn't find a path to the following filter : " << getFilter() << LL_ENDL; - } - } - // Scale to a power of 2 so it can be mapped to a texture - raw->expandToPowerOfTwo(); - mBigThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), false); - mBigThumbnailUpToDate = true ; - } - - return mBigThumbnailImage ; -} - -// Called often. Checks whether it's time to grab a new snapshot and if so, does it. -// Returns true if new snapshot generated, false otherwise. -//static -bool LLSnapshotLivePreview::onIdle( void* snapshot_preview ) -{ - LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)snapshot_preview; - if (previewp->getWidth() == 0 || previewp->getHeight() == 0) - { - LL_WARNS("Snapshot") << "Incorrect dimensions: " << previewp->getWidth() << "x" << previewp->getHeight() << LL_ENDL; - return false; - } - - if (previewp->mSnapshotDelayTimer.getStarted()) // Wait for a snapshot delay timer - { - if (!previewp->mSnapshotDelayTimer.hasExpired()) - { - return false; - } - previewp->mSnapshotDelayTimer.stop(); - } - - if (LLToolCamera::getInstance()->hasMouseCapture()) // Hide full-screen preview while camming, either don't take snapshots while ALT-zoom active - { - previewp->setVisible(false); - return false; - } - - // If we're in freeze-frame and/or auto update mode and camera has moved, update snapshot. - LLVector3 new_camera_pos = LLViewerCamera::getInstance()->getOrigin(); - LLQuaternion new_camera_rot = LLViewerCamera::getInstance()->getQuaternion(); - if (previewp->mForceUpdateSnapshot || - (((gSavedSettings.getBOOL("AutoSnapshot") && LLView::isAvailable(previewp->mViewContainer)) || - (gSavedSettings.getBOOL("FreezeTime") && previewp->mAllowFullScreenPreview)) && - (new_camera_pos != previewp->mCameraPos || dot(new_camera_rot, previewp->mCameraRot) < 0.995f))) - { - previewp->mCameraPos = new_camera_pos; - previewp->mCameraRot = new_camera_rot; - // request a new snapshot whenever the camera moves, with a time delay - bool new_snapshot = gSavedSettings.getBOOL("AutoSnapshot") || previewp->mForceUpdateSnapshot; - LL_DEBUGS("Snapshot") << "camera moved, updating thumbnail" << LL_ENDL; - previewp->updateSnapshot( - new_snapshot, // whether a new snapshot is needed or merely invalidate the existing one - false, // or if 1st arg is false, whether to produce a new thumbnail image. - new_snapshot ? AUTO_SNAPSHOT_TIME_DELAY : 0.f); // shutter delay if 1st arg is true. - previewp->mForceUpdateSnapshot = false; - } - - if (previewp->getSnapshotUpToDate() && previewp->getThumbnailUpToDate()) - { - return false; - } - - // time to produce a snapshot - if(!previewp->getSnapshotUpToDate()) - { - LL_DEBUGS("Snapshot") << "producing snapshot" << LL_ENDL; - if (!previewp->mPreviewImage) - { - previewp->mPreviewImage = new LLImageRaw; - } - - previewp->mSnapshotActive = true; - - previewp->setVisible(false); - previewp->setEnabled(false); - - previewp->getWindow()->incBusyCount(); - previewp->setImageScaled(false); - - // grab the raw image - if (gViewerWindow->rawSnapshot( - previewp->mPreviewImage, - previewp->getWidth(), - previewp->getHeight(), - previewp->mKeepAspectRatio,//gSavedSettings.getBOOL("KeepAspectForSnapshot"), - previewp->getSnapshotType() == LLSnapshotModel::SNAPSHOT_TEXTURE, - previewp->mAllowRenderUI && gSavedSettings.getBOOL("RenderUIInSnapshot"), - gSavedSettings.getBOOL("RenderHUDInSnapshot"), - false, - gSavedSettings.getBOOL("RenderSnapshotNoPost"), - previewp->mSnapshotBufferType, - previewp->getMaxImageSize())) - { - // Invalidate/delete any existing encoded image - previewp->mPreviewImageEncoded = NULL; - // Invalidate/delete any existing formatted image - previewp->mFormattedImage = NULL; - // Update the data size - previewp->estimateDataSize(); - - // Full size preview is set: get the decoded image result and save it for animation - if (gSavedSettings.getBOOL("UseFreezeFrame") && previewp->mAllowFullScreenPreview) - { - previewp->prepareFreezeFrame(); - } - - // The snapshot is updated now... - previewp->mSnapshotUpToDate = true; - - // We need to update the thumbnail though - previewp->setThumbnailImageSize(); - previewp->generateThumbnailImage(true) ; - } - previewp->getWindow()->decBusyCount(); - previewp->setVisible(gSavedSettings.getBOOL("UseFreezeFrame") && previewp->mAllowFullScreenPreview); // only show fullscreen preview when in freeze frame mode - previewp->mSnapshotActive = false; - LL_DEBUGS("Snapshot") << "done creating snapshot" << LL_ENDL; - } - - if (!previewp->getThumbnailUpToDate()) - { - previewp->generateThumbnailImage() ; - } - - // Tell the floater container that the snapshot is updated now - if (previewp->mViewContainer) - { - previewp->mViewContainer->notify(LLSD().with("snapshot-updated", true)); - } - - return true; -} - -void LLSnapshotLivePreview::prepareFreezeFrame() -{ - // Get the decoded version of the formatted image - getEncodedImage(); - - LLImageDataSharedLock lock(mPreviewImageEncoded); - - // We need to scale that a bit for display... - LLPointer scaled = new LLImageRaw( - mPreviewImageEncoded->getData(), - mPreviewImageEncoded->getWidth(), - mPreviewImageEncoded->getHeight(), - mPreviewImageEncoded->getComponents()); - - if (!scaled->isBufferInvalid()) - { - // leave original image dimensions, just scale up texture buffer - if (mPreviewImageEncoded->getWidth() > 1024 || mPreviewImageEncoded->getHeight() > 1024) - { - // go ahead and shrink image to appropriate power of 2 for display - scaled->biasedScaleToPowerOfTwo(1024); - setImageScaled(true); - } - else - { - // expand image but keep original image data intact - scaled->expandToPowerOfTwo(1024, false); - } - - mViewerImage[mCurImageIndex] = LLViewerTextureManager::getLocalTexture(scaled.get(), false); - LLPointer curr_preview_image = mViewerImage[mCurImageIndex]; - gGL.getTexUnit(0)->bind(curr_preview_image); - curr_preview_image->setFilteringOption(getSnapshotType() == LLSnapshotModel::SNAPSHOT_TEXTURE ? LLTexUnit::TFO_ANISOTROPIC : LLTexUnit::TFO_POINT); - curr_preview_image->setAddressMode(LLTexUnit::TAM_CLAMP); - - - if (gSavedSettings.getBOOL("UseFreezeFrame") && mAllowFullScreenPreview) - { - mShineCountdown = 4; // wait a few frames to avoid animation glitch due to readback this frame - } - } -} - -S32 LLSnapshotLivePreview::getEncodedImageWidth() const -{ - S32 width = getWidth(); - if (getSnapshotType() == LLSnapshotModel::SNAPSHOT_TEXTURE) - { - width = LLImageRaw::biasedDimToPowerOfTwo(width,MAX_TEXTURE_SIZE); - } - return width; -} -S32 LLSnapshotLivePreview::getEncodedImageHeight() const -{ - S32 height = getHeight(); - if (getSnapshotType() == LLSnapshotModel::SNAPSHOT_TEXTURE) - { - height = LLImageRaw::biasedDimToPowerOfTwo(height,MAX_TEXTURE_SIZE); - } - return height; -} - -LLPointer LLSnapshotLivePreview::getEncodedImage() -{ - if (!mPreviewImageEncoded) - { - LLImageDataSharedLock lock(mPreviewImage); - - mPreviewImageEncoded = new LLImageRaw; - - mPreviewImageEncoded->resize( - mPreviewImage->getWidth(), - mPreviewImage->getHeight(), - mPreviewImage->getComponents()); - - if (getSnapshotType() == LLSnapshotModel::SNAPSHOT_TEXTURE) - { - // We don't store the intermediate formatted image in mFormattedImage in the J2C case - LL_DEBUGS("Snapshot") << "Encoding new image of format J2C" << LL_ENDL; - LLPointer formatted = new LLImageJ2C; - // Copy the preview - LLPointer scaled = new LLImageRaw( - mPreviewImage->getData(), - mPreviewImage->getWidth(), - mPreviewImage->getHeight(), - mPreviewImage->getComponents()); - // Scale it as required by J2C - scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE); - setImageScaled(true); - // Compress to J2C - if (formatted->encode(scaled, 0.f)) - { - // We can update the data size precisely at that point - mDataSize = formatted->getDataSize(); - // Decompress back - formatted->decode(mPreviewImageEncoded, 0); - } - } - else - { - // Update mFormattedImage if necessary - getFormattedImage(); - if (getSnapshotFormat() == LLSnapshotModel::SNAPSHOT_FORMAT_BMP) - { - // BMP hack : copy instead of decode otherwise decode will crash. - mPreviewImageEncoded->copy(mPreviewImage); - } - else - { - // Decode back - mFormattedImage->decode(mPreviewImageEncoded, 0); - } - } - } - return mPreviewImageEncoded; -} - -bool LLSnapshotLivePreview::createUploadFile(const std::string &out_filename, const S32 max_image_dimentions, const S32 min_image_dimentions) -{ - 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 -// Note : whenever formatted image is computed, mDataSize will be updated to reflect the true size -void LLSnapshotLivePreview::estimateDataSize() -{ - // Compression ratio - F32 ratio = 1.0; - - if (getSnapshotType() == LLSnapshotModel::SNAPSHOT_TEXTURE) - { - ratio = 8.0; // This is what we shoot for when compressing to J2C - } - else - { - LLSnapshotModel::ESnapshotFormat format = getSnapshotFormat(); - switch (format) - { - case LLSnapshotModel::SNAPSHOT_FORMAT_PNG: - ratio = 3.0; // Average observed PNG compression ratio - break; - case LLSnapshotModel::SNAPSHOT_FORMAT_JPEG: - // Observed from JPG compression tests - ratio = (110 - mSnapshotQuality) / 2; - break; - case LLSnapshotModel::SNAPSHOT_FORMAT_BMP: - ratio = 1.0; // No compression with BMP - break; - } - } - mDataSize = (S32)((F32)mPreviewImage->getDataSize() / ratio); -} - -LLPointer LLSnapshotLivePreview::getFormattedImage() -{ - if (!mFormattedImage) - { - // Apply the filter to mPreviewImage - if (getFilter() != "") - { - std::string filter_path = LLImageFiltersManager::getInstance()->getFilterPath(getFilter()); - if (filter_path != "") - { - LLImageFilter filter(filter_path); - filter.executeFilter(mPreviewImage); - } - else - { - LL_WARNS("Snapshot") << "Couldn't find a path to the following filter : " << getFilter() << LL_ENDL; - } - } - - // Create the new formatted image of the appropriate format. - LLSnapshotModel::ESnapshotFormat format = getSnapshotFormat(); - LL_DEBUGS("Snapshot") << "Encoding new image of format " << format << LL_ENDL; - - switch (format) - { - case LLSnapshotModel::SNAPSHOT_FORMAT_PNG: - mFormattedImage = new LLImagePNG(); - break; - case LLSnapshotModel::SNAPSHOT_FORMAT_JPEG: - mFormattedImage = new LLImageJPEG(mSnapshotQuality); - break; - case LLSnapshotModel::SNAPSHOT_FORMAT_BMP: - mFormattedImage = new LLImageBMP(); - break; - } - if (mFormattedImage->encode(mPreviewImage, 0)) - { - // We can update the data size precisely at that point - mDataSize = mFormattedImage->getDataSize(); - } - } - return mFormattedImage; -} - -void LLSnapshotLivePreview::setSize(S32 w, S32 h) -{ - LL_DEBUGS("Snapshot") << "setSize(" << w << ", " << h << ")" << LL_ENDL; - setWidth(w); - setHeight(h); -} - -void LLSnapshotLivePreview::setSnapshotFormat(LLSnapshotModel::ESnapshotFormat format) -{ - if (mSnapshotFormat != format) - { - mFormattedImage = NULL; // Invalidate the already formatted image if any - mSnapshotFormat = format; - } -} - -void LLSnapshotLivePreview::getSize(S32& w, S32& h) const -{ - w = getWidth(); - h = getHeight(); -} - -void LLSnapshotLivePreview::saveTexture(bool outfit_snapshot, std::string name) -{ - LLImageDataSharedLock lock(mPreviewImage); - - LL_DEBUGS("Snapshot") << "saving texture: " << mPreviewImage->getWidth() << "x" << mPreviewImage->getHeight() << LL_ENDL; - // gen a new uuid for this asset - LLTransactionID tid; - tid.generate(); - LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); - - LLPointer formatted = new LLImageJ2C; - LLPointer scaled = new LLImageRaw(mPreviewImage->getData(), - mPreviewImage->getWidth(), - mPreviewImage->getHeight(), - mPreviewImage->getComponents()); - - // Apply the filter to mPreviewImage - if (getFilter() != "") - { - std::string filter_path = LLImageFiltersManager::getInstance()->getFilterPath(getFilter()); - if (filter_path != "") - { - LLImageFilter filter(filter_path); - filter.executeFilter(scaled); - } - else - { - LL_WARNS("Snapshot") << "Couldn't find a path to the following filter : " << getFilter() << LL_ENDL; - } - } - - scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE); - LL_DEBUGS("Snapshot") << "scaled texture to " << scaled->getWidth() << "x" << scaled->getHeight() << LL_ENDL; - - if (formatted->encode(scaled, 0.0f)) - { - LLFileSystem fmt_file(new_asset_id, LLAssetType::AT_TEXTURE, LLFileSystem::WRITE); - fmt_file.write(formatted->getData(), formatted->getDataSize()); - std::string pos_string; - LLAgentUI::buildLocationString(pos_string, LLAgentUI::LOCATION_FORMAT_FULL); - std::string who_took_it; - LLAgentUI::buildFullname(who_took_it); - S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(); - std::string res_name = outfit_snapshot ? name : "Snapshot : " + pos_string; - std::string res_desc = outfit_snapshot ? "" : "Taken by " + who_took_it + " at " + pos_string; - LLFolderType::EType folder_type = outfit_snapshot ? LLFolderType::FT_NONE : LLFolderType::FT_SNAPSHOT_CATEGORY; - LLInventoryType::EType inv_type = outfit_snapshot ? LLInventoryType::IT_NONE : LLInventoryType::IT_SNAPSHOT; - - LLResourceUploadInfo::ptr_t assetUploadInfo(new LLResourceUploadInfo( - tid, LLAssetType::AT_TEXTURE, res_name, res_desc, 0, - folder_type, inv_type, - PERM_ALL, LLFloaterPerms::getGroupPerms("Uploads"), LLFloaterPerms::getEveryonePerms("Uploads"), - expected_upload_cost, !outfit_snapshot)); - - upload_new_resource(assetUploadInfo); - - gViewerWindow->playSnapshotAnimAndSound(); - } - else - { - LLNotificationsUtil::add("ErrorEncodingSnapshot"); - LL_WARNS("Snapshot") << "Error encoding snapshot" << LL_ENDL; - } - - add(LLStatViewer::SNAPSHOT, 1); - - mDataSize = 0; -} - -void LLSnapshotLivePreview::saveLocal(const snapshot_saved_signal_t::slot_type& success_cb, const snapshot_saved_signal_t::slot_type& failure_cb) -{ - // Update mFormattedImage if necessary - getFormattedImage(); - - // Save the formatted image - saveLocal(mFormattedImage, success_cb, failure_cb); -} - -//Check if failed due to insufficient memory -void LLSnapshotLivePreview::saveLocal(LLPointer image, const snapshot_saved_signal_t::slot_type& success_cb, const snapshot_saved_signal_t::slot_type& failure_cb) -{ - sSaveLocalImage = image; - - gViewerWindow->saveImageNumbered(sSaveLocalImage, false, success_cb, failure_cb); -} +/** +* @file llsnapshotlivepreview.cpp +* @brief Implementation of llsnapshotlivepreview +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2014, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#include "llviewerprecompiledheaders.h" + +#include "llagent.h" +#include "llagentbenefits.h" +#include "llagentcamera.h" +#include "llagentui.h" +#include "llfilesystem.h" +#include "llcombobox.h" +#include "llfloaterperms.h" +#include "llfloaterreg.h" +#include "llimagefilter.h" +#include "llimagefiltersmanager.h" +#include "llimagebmp.h" +#include "llimagej2c.h" +#include "llimagejpeg.h" +#include "llimagepng.h" +#include "lllandmarkactions.h" +#include "lllocalcliprect.h" +#include "llresmgr.h" +#include "llnotificationsutil.h" +#include "llslurl.h" +#include "llsnapshotlivepreview.h" +#include "lltoolfocus.h" +#include "llviewercontrol.h" +#include "llviewermenufile.h" // upload_new_resource() +#include "llviewerstats.h" +#include "llviewertexturelist.h" +#include "llwindow.h" +#include "llworld.h" +#include + +constexpr F32 AUTO_SNAPSHOT_TIME_DELAY = 1.f; + +constexpr F32 SHINE_TIME = 0.5f; +constexpr F32 SHINE_WIDTH = 0.6f; +constexpr F32 SHINE_OPACITY = 0.3f; +constexpr F32 FALL_TIME = 0.6f; +constexpr S32 BORDER_WIDTH = 6; +constexpr S32 TOP_PANEL_HEIGHT = 30; + +constexpr S32 MAX_TEXTURE_SIZE = 512 ; //max upload texture size 512 * 512 + +std::set LLSnapshotLivePreview::sList; +LLPointer LLSnapshotLivePreview::sSaveLocalImage = NULL; + +LLSnapshotLivePreview::LLSnapshotLivePreview (const LLSnapshotLivePreview::Params& p) + : LLView(p), + mColor(1.f, 0.f, 0.f, 0.5f), + mCurImageIndex(0), + mPreviewImage(NULL), + mThumbnailImage(NULL) , + mBigThumbnailImage(NULL) , + mThumbnailWidth(0), + mThumbnailHeight(0), + mThumbnailSubsampled(false), + mPreviewImageEncoded(NULL), + mFormattedImage(NULL), + mShineCountdown(0), + mFlashAlpha(0.f), + mNeedsFlash(true), + mSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")), + mDataSize(0), + mSnapshotType(LLSnapshotModel::SNAPSHOT_POSTCARD), + mSnapshotFormat(LLSnapshotModel::ESnapshotFormat(gSavedSettings.getS32("SnapshotFormat"))), + mSnapshotUpToDate(false), + mCameraPos(LLViewerCamera::getInstance()->getOrigin()), + mCameraRot(LLViewerCamera::getInstance()->getQuaternion()), + mSnapshotActive(false), + mSnapshotBufferType(LLSnapshotModel::SNAPSHOT_TYPE_COLOR), + mFilterName(""), + mAllowRenderUI(true), + mAllowFullScreenPreview(true), + mViewContainer(NULL) +{ + setSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")); + mSnapshotDelayTimer.setTimerExpirySec(0.0f); + mSnapshotDelayTimer.start(); + // gIdleCallbacks.addFunction( &LLSnapshotLivePreview::onIdle, (void*)this ); + sList.insert(this); + setFollowsAll(); + mWidth[0] = gViewerWindow->getWindowWidthRaw(); + mWidth[1] = gViewerWindow->getWindowWidthRaw(); + mHeight[0] = gViewerWindow->getWindowHeightRaw(); + mHeight[1] = gViewerWindow->getWindowHeightRaw(); + mImageScaled[0] = false; + mImageScaled[1] = false; + + mMaxImageSize = MAX_SNAPSHOT_IMAGE_SIZE ; + mKeepAspectRatio = gSavedSettings.getBOOL("KeepAspectForSnapshot") ; + mThumbnailUpdateLock = false ; + mThumbnailUpToDate = false ; + mBigThumbnailUpToDate = false ; + + mForceUpdateSnapshot = false; +} + +LLSnapshotLivePreview::~LLSnapshotLivePreview() +{ + // delete images + mPreviewImage = NULL; + mPreviewImageEncoded = NULL; + mFormattedImage = NULL; + + // gIdleCallbacks.deleteFunction( &LLSnapshotLivePreview::onIdle, (void*)this ); + sList.erase(this); + sSaveLocalImage = NULL; +} + +void LLSnapshotLivePreview::setMaxImageSize(S32 size) +{ + mMaxImageSize = llmin(size,(S32)(MAX_SNAPSHOT_IMAGE_SIZE)); +} + +LLViewerTexture* LLSnapshotLivePreview::getCurrentImage() +{ + return mViewerImage[mCurImageIndex]; +} + +F32 LLSnapshotLivePreview::getImageAspect() +{ + if (!getCurrentImage()) + { + return 0.f; + } + // mKeepAspectRatio) == gSavedSettings.getBOOL("KeepAspectForSnapshot")) + return (mKeepAspectRatio ? ((F32)getRect().getWidth()) / ((F32)getRect().getHeight()) : ((F32)getWidth()) / ((F32)getHeight())); +} + +void LLSnapshotLivePreview::updateSnapshot(bool new_snapshot, bool new_thumbnail, F32 delay) +{ + LL_DEBUGS("Snapshot") << "updateSnapshot: mSnapshotUpToDate = " << getSnapshotUpToDate() << LL_ENDL; + + // Update snapshot if requested. + if (new_snapshot) + { + if (getSnapshotUpToDate()) + { + S32 old_image_index = mCurImageIndex; + mCurImageIndex = (mCurImageIndex + 1) % 2; + setSize(mWidth[old_image_index], mHeight[old_image_index]); + mFallAnimTimer.start(); + } + mSnapshotUpToDate = false; + + // Update snapshot source rect depending on whether we keep the aspect ratio. + LLRect& rect = mImageRect[mCurImageIndex]; + rect.set(0, getRect().getHeight(), getRect().getWidth(), 0); + + F32 image_aspect_ratio = ((F32)getWidth()) / ((F32)getHeight()); + F32 window_aspect_ratio = ((F32)getRect().getWidth()) / ((F32)getRect().getHeight()); + + if (mKeepAspectRatio)//gSavedSettings.getBOOL("KeepAspectForSnapshot")) + { + if (image_aspect_ratio > window_aspect_ratio) + { + // trim off top and bottom + S32 new_height = ll_round((F32)getRect().getWidth() / image_aspect_ratio); + rect.mBottom += (getRect().getHeight() - new_height) / 2; + rect.mTop -= (getRect().getHeight() - new_height) / 2; + } + else if (image_aspect_ratio < window_aspect_ratio) + { + // trim off left and right + S32 new_width = ll_round((F32)getRect().getHeight() * image_aspect_ratio); + rect.mLeft += (getRect().getWidth() - new_width) / 2; + rect.mRight -= (getRect().getWidth() - new_width) / 2; + } + } + + // Stop shining animation. + mShineAnimTimer.stop(); + mSnapshotDelayTimer.start(); + mSnapshotDelayTimer.resetWithExpiry(delay); + + + mPosTakenGlobal = gAgentCamera.getCameraPositionGlobal(); + + // Tell the floater container that the snapshot is in the process of updating itself + if (mViewContainer) + { + mViewContainer->notify(LLSD().with("snapshot-updating", true)); + } + } + + // Update thumbnail if requested. + if (new_thumbnail) + { + mThumbnailUpToDate = false ; + mBigThumbnailUpToDate = false; + } +} + +// Return true if the quality has been changed, false otherwise +bool LLSnapshotLivePreview::setSnapshotQuality(S32 quality, bool set_by_user) +{ + llclamp(quality, 0, 100); + if (quality != mSnapshotQuality) + { + mSnapshotQuality = quality; + if (set_by_user) + { + gSavedSettings.setS32("SnapshotQuality", quality); + } + mFormattedImage = NULL; // Invalidate the already formatted image if any + return true; + } + return false; +} + +void LLSnapshotLivePreview::drawPreviewRect(S32 offset_x, S32 offset_y, LLColor4 alpha_color) +{ + F32 line_width ; + glGetFloatv(GL_LINE_WIDTH, &line_width) ; + glLineWidth(2.0f * line_width) ; + LLColor4 color(0.0f, 0.0f, 0.0f, 1.0f) ; + gl_rect_2d( mPreviewRect.mLeft + offset_x, mPreviewRect.mTop + offset_y, + mPreviewRect.mRight + offset_x, mPreviewRect.mBottom + offset_y, color, false ) ; + glLineWidth(line_width) ; + + //draw four alpha rectangles to cover areas outside of the snapshot image + if(!mKeepAspectRatio) + { + S32 dwl = 0, dwr = 0 ; + if(mThumbnailWidth > mPreviewRect.getWidth()) + { + dwl = (mThumbnailWidth - mPreviewRect.getWidth()) >> 1 ; + dwr = mThumbnailWidth - mPreviewRect.getWidth() - dwl ; + + gl_rect_2d(mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mTop + offset_y, + mPreviewRect.mLeft + offset_x, mPreviewRect.mBottom + offset_y, alpha_color, true ) ; + gl_rect_2d( mPreviewRect.mRight + offset_x, mPreviewRect.mTop + offset_y, + mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y, alpha_color, true ) ; + } + + if(mThumbnailHeight > mPreviewRect.getHeight()) + { + S32 dh = (mThumbnailHeight - mPreviewRect.getHeight()) >> 1 ; + gl_rect_2d(mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mBottom + offset_y , + mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y - dh, alpha_color, true ) ; + + dh = mThumbnailHeight - mPreviewRect.getHeight() - dh ; + gl_rect_2d( mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mTop + offset_y + dh, + mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mTop + offset_y, alpha_color, true ) ; + } + } +} + +//called when the frame is frozen. +void LLSnapshotLivePreview::draw() +{ + if (getCurrentImage() && + mPreviewImageEncoded.notNull() && + getSnapshotUpToDate()) + { + LLColor4 bg_color(0.f, 0.f, 0.3f, 0.4f); + gl_rect_2d(getRect(), bg_color); + const LLRect& rect = getImageRect(); + LLRect shadow_rect = rect; + shadow_rect.stretch(BORDER_WIDTH); + gl_drop_shadow(shadow_rect.mLeft, shadow_rect.mTop, shadow_rect.mRight, shadow_rect.mBottom, LLColor4(0.f, 0.f, 0.f, mNeedsFlash ? 0.f :0.5f), 10); + + LLColor4 image_color(1.f, 1.f, 1.f, 1.f); + gGL.color4fv(image_color.mV); + gGL.getTexUnit(0)->bind(getCurrentImage()); + // calculate UV scale + F32 uv_width = isImageScaled() ? 1.f : llmin((F32)getWidth() / (F32)getCurrentImage()->getWidth(), 1.f); + F32 uv_height = isImageScaled() ? 1.f : llmin((F32)getHeight() / (F32)getCurrentImage()->getHeight(), 1.f); + gGL.pushMatrix(); + { + gGL.translatef((F32)rect.mLeft, (F32)rect.mBottom + TOP_PANEL_HEIGHT, 0.f); + gGL.begin(LLRender::QUADS); + { + gGL.texCoord2f(uv_width, uv_height); + gGL.vertex2i(rect.getWidth(), rect.getHeight() ); + + gGL.texCoord2f(0.f, uv_height); + gGL.vertex2i(0, rect.getHeight() ); + + gGL.texCoord2f(0.f, 0.f); + gGL.vertex2i(0, 0); + + gGL.texCoord2f(uv_width, 0.f); + gGL.vertex2i(rect.getWidth(), 0); + } + gGL.end(); + } + gGL.popMatrix(); + + gGL.color4f(1.f, 1.f, 1.f, mFlashAlpha); + gl_rect_2d(getRect()); + if (mNeedsFlash) + { + if (mFlashAlpha < 1.f) + { + mFlashAlpha = lerp(mFlashAlpha, 1.f, LLCriticalDamp::getInterpolant(0.02f)); + } + else + { + mNeedsFlash = false; + } + } + else + { + mFlashAlpha = lerp(mFlashAlpha, 0.f, LLCriticalDamp::getInterpolant(0.15f)); + } + + // Draw shining animation if appropriate. + if (mShineCountdown > 0) + { + mShineCountdown--; + if (mShineCountdown == 0) + { + mShineAnimTimer.start(); + } + } + else if (mShineAnimTimer.getStarted()) + { + LL_DEBUGS("Snapshot") << "Drawing shining animation" << LL_ENDL; + F32 shine_interp = llmin(1.f, mShineAnimTimer.getElapsedTimeF32() / SHINE_TIME); + + // draw "shine" effect + LLRect local_rect(0, getRect().getHeight() + TOP_PANEL_HEIGHT, getRect().getWidth(), 0); + LLLocalClipRect clip(local_rect); + { + // draw diagonal stripe with gradient that passes over screen + S32 x1 = gViewerWindow->getWindowWidthScaled() * ll_round((clamp_rescale(shine_interp, 0.f, 1.f, -1.f - SHINE_WIDTH, 1.f))); + S32 x2 = x1 + ll_round(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH); + S32 x3 = x2 + ll_round(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH); + S32 y1 = 0; + S32 y2 = gViewerWindow->getWindowHeightScaled() + TOP_PANEL_HEIGHT; + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.begin(LLRender::QUADS); + { + gGL.color4f(1.f, 1.f, 1.f, 0.f); + gGL.vertex2i(x1, y1); + gGL.vertex2i(x1 + gViewerWindow->getWindowWidthScaled(), y2); + gGL.color4f(1.f, 1.f, 1.f, SHINE_OPACITY); + gGL.vertex2i(x2 + gViewerWindow->getWindowWidthScaled(), y2); + gGL.vertex2i(x2, y1); + + gGL.color4f(1.f, 1.f, 1.f, SHINE_OPACITY); + gGL.vertex2i(x2, y1); + gGL.vertex2i(x2 + gViewerWindow->getWindowWidthScaled(), y2); + gGL.color4f(1.f, 1.f, 1.f, 0.f); + gGL.vertex2i(x3 + gViewerWindow->getWindowWidthScaled(), y2); + gGL.vertex2i(x3, y1); + } + gGL.end(); + } + + // if we're at the end of the animation, stop + if (shine_interp >= 1.f) + { + mShineAnimTimer.stop(); + } + } + } + + // draw old image dropping away + if (mFallAnimTimer.getStarted()) + { + S32 old_image_index = (mCurImageIndex + 1) % 2; + if (mViewerImage[old_image_index].notNull() && mFallAnimTimer.getElapsedTimeF32() < FALL_TIME) + { + LL_DEBUGS("Snapshot") << "Drawing fall animation" << LL_ENDL; + F32 fall_interp = mFallAnimTimer.getElapsedTimeF32() / FALL_TIME; + F32 alpha = clamp_rescale(fall_interp, 0.f, 1.f, 0.8f, 0.4f); + LLColor4 image_color(1.f, 1.f, 1.f, alpha); + gGL.color4fv(image_color.mV); + gGL.getTexUnit(0)->bind(mViewerImage[old_image_index]); + // calculate UV scale + // *FIX get this to work with old image + bool rescale = !mImageScaled[old_image_index] && mViewerImage[mCurImageIndex].notNull(); + F32 uv_width = rescale ? llmin((F32)mWidth[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getWidth(), 1.f) : 1.f; + F32 uv_height = rescale ? llmin((F32)mHeight[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getHeight(), 1.f) : 1.f; + gGL.pushMatrix(); + { + LLRect& rect = mImageRect[old_image_index]; + gGL.translatef((F32)rect.mLeft, (F32)rect.mBottom - ll_round(getRect().getHeight() * 2.f * (fall_interp * fall_interp)), 0.f); + gGL.rotatef(-45.f * fall_interp, 0.f, 0.f, 1.f); + gGL.begin(LLRender::QUADS); + { + gGL.texCoord2f(uv_width, uv_height); + gGL.vertex2i(rect.getWidth(), rect.getHeight() ); + + gGL.texCoord2f(0.f, uv_height); + gGL.vertex2i(0, rect.getHeight() ); + + gGL.texCoord2f(0.f, 0.f); + gGL.vertex2i(0, 0); + + gGL.texCoord2f(uv_width, 0.f); + gGL.vertex2i(rect.getWidth(), 0); + } + gGL.end(); + } + gGL.popMatrix(); + } + } +} + +/*virtual*/ +void LLSnapshotLivePreview::reshape(S32 width, S32 height, bool called_from_parent) +{ + LLRect old_rect = getRect(); + LLView::reshape(width, height, called_from_parent); + if (old_rect.getWidth() != width || old_rect.getHeight() != height) + { + LL_DEBUGS("Window", "Snapshot") << "window reshaped, updating thumbnail" << LL_ENDL; + if (mViewContainer && mViewContainer->isInVisibleChain()) + { + // We usually resize only on window reshape, so give it a chance to redraw, assign delay + updateSnapshot( + true, // new snapshot is needed + false, // thumbnail will be updated either way. + AUTO_SNAPSHOT_TIME_DELAY); // shutter delay. + } + } +} + +bool LLSnapshotLivePreview::setThumbnailImageSize() +{ + if (getWidth() < 10 || getHeight() < 10) + { + return false ; + } + S32 width = (mThumbnailSubsampled ? mPreviewImage->getWidth() : gViewerWindow->getWindowWidthRaw()); + S32 height = (mThumbnailSubsampled ? mPreviewImage->getHeight() : gViewerWindow->getWindowHeightRaw()) ; + + F32 aspect_ratio = ((F32)width) / ((F32)height); + + // UI size for thumbnail + S32 max_width = mThumbnailPlaceholderRect.getWidth(); + S32 max_height = mThumbnailPlaceholderRect.getHeight(); + + if (aspect_ratio > (F32)max_width / (F32)max_height) + { + // image too wide, shrink to width + mThumbnailWidth = max_width; + mThumbnailHeight = ll_round((F32)max_width / aspect_ratio); + } + else + { + // image too tall, shrink to height + mThumbnailHeight = max_height; + mThumbnailWidth = ll_round((F32)max_height * aspect_ratio); + } + + if (mThumbnailWidth > width || mThumbnailHeight > height) + { + return false ;//if the window is too small, ignore thumbnail updating. + } + + S32 left = 0 , top = mThumbnailHeight, right = mThumbnailWidth, bottom = 0 ; + if (!mKeepAspectRatio) + { + F32 ratio_x = (F32)getWidth() / width ; + F32 ratio_y = (F32)getHeight() / height ; + + if (ratio_x > ratio_y) + { + top = (S32)(top * ratio_y / ratio_x) ; + } + else + { + right = (S32)(right * ratio_x / ratio_y) ; + } + left = (S32)((mThumbnailWidth - right) * 0.5f) ; + bottom = (S32)((mThumbnailHeight - top) * 0.5f) ; + top += bottom ; + right += left ; + } + mPreviewRect.set(left - 1, top + 1, right + 1, bottom - 1) ; + + return true ; +} + +void LLSnapshotLivePreview::generateThumbnailImage(bool force_update) +{ + if(mThumbnailUpdateLock) //in the process of updating + { + return ; + } + if(getThumbnailUpToDate() && !force_update)//already updated + { + return ; + } + if(getWidth() < 10 || getHeight() < 10) + { + return ; + } + + ////lock updating + mThumbnailUpdateLock = true ; + + if(!setThumbnailImageSize()) + { + mThumbnailUpdateLock = false ; + mThumbnailUpToDate = true ; + return ; + } + + // Invalidate the big thumbnail when we regenerate the small one + mBigThumbnailUpToDate = false; + + if(mThumbnailImage) + { + resetThumbnailImage() ; + } + + LLPointer raw = new LLImageRaw; + + if (mThumbnailSubsampled) + { + // The thumbnail is be a subsampled version of the preview (used in SL Share previews, i.e. Flickr, Twitter) + raw->resize( mPreviewImage->getWidth(), + mPreviewImage->getHeight(), + mPreviewImage->getComponents()); + raw->copy(mPreviewImage); + // Scale to the thumbnail size + if (!raw->scale(mThumbnailWidth, mThumbnailHeight)) + { + raw = NULL ; + } + } + else + { + // The thumbnail is a screen view with screen grab positioning preview + if(!gViewerWindow->thumbnailSnapshot(raw, + mThumbnailWidth, mThumbnailHeight, + mAllowRenderUI && gSavedSettings.getBOOL("RenderUIInSnapshot"), + gSavedSettings.getBOOL("RenderHUDInSnapshot"), + false, + gSavedSettings.getBOOL("RenderSnapshotNoPost"), + mSnapshotBufferType) ) + { + raw = NULL ; + } + } + + if (raw) + { + // Filter the thumbnail + // Note: filtering needs to be done *before* the scaling to power of 2 or the effect is distorted + if (getFilter() != "") + { + std::string filter_path = LLImageFiltersManager::getInstance()->getFilterPath(getFilter()); + if (filter_path != "") + { + LLImageFilter filter(filter_path); + filter.executeFilter(raw); + } + else + { + LL_WARNS("Snapshot") << "Couldn't find a path to the following filter : " << getFilter() << LL_ENDL; + } + } + // Scale to a power of 2 so it can be mapped to a texture + raw->expandToPowerOfTwo(); + mThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), false); + mThumbnailUpToDate = true ; + } + + //unlock updating + mThumbnailUpdateLock = false ; +} + +LLViewerTexture* LLSnapshotLivePreview::getBigThumbnailImage() +{ + if (mThumbnailUpdateLock) //in the process of updating + { + return NULL; + } + if (mBigThumbnailUpToDate && mBigThumbnailImage)//already updated + { + return mBigThumbnailImage; + } + + LLPointer raw = new LLImageRaw; + + if (raw) + { + // The big thumbnail is a new filtered version of the preview (used in SL Share previews, i.e. Flickr, Twitter) + mBigThumbnailWidth = mPreviewImage->getWidth(); + mBigThumbnailHeight = mPreviewImage->getHeight(); + raw->resize( mBigThumbnailWidth, + mBigThumbnailHeight, + mPreviewImage->getComponents()); + raw->copy(mPreviewImage); + + // Filter + // Note: filtering needs to be done *before* the scaling to power of 2 or the effect is distorted + if (getFilter() != "") + { + std::string filter_path = LLImageFiltersManager::getInstance()->getFilterPath(getFilter()); + if (filter_path != "") + { + LLImageFilter filter(filter_path); + filter.executeFilter(raw); + } + else + { + LL_WARNS("Snapshot") << "Couldn't find a path to the following filter : " << getFilter() << LL_ENDL; + } + } + // Scale to a power of 2 so it can be mapped to a texture + raw->expandToPowerOfTwo(); + mBigThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), false); + mBigThumbnailUpToDate = true ; + } + + return mBigThumbnailImage ; +} + +// Called often. Checks whether it's time to grab a new snapshot and if so, does it. +// Returns true if new snapshot generated, false otherwise. +//static +bool LLSnapshotLivePreview::onIdle( void* snapshot_preview ) +{ + LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)snapshot_preview; + if (previewp->getWidth() == 0 || previewp->getHeight() == 0) + { + LL_WARNS("Snapshot") << "Incorrect dimensions: " << previewp->getWidth() << "x" << previewp->getHeight() << LL_ENDL; + return false; + } + + if (previewp->mSnapshotDelayTimer.getStarted()) // Wait for a snapshot delay timer + { + if (!previewp->mSnapshotDelayTimer.hasExpired()) + { + return false; + } + previewp->mSnapshotDelayTimer.stop(); + } + + if (LLToolCamera::getInstance()->hasMouseCapture()) // Hide full-screen preview while camming, either don't take snapshots while ALT-zoom active + { + previewp->setVisible(false); + return false; + } + + // If we're in freeze-frame and/or auto update mode and camera has moved, update snapshot. + LLVector3 new_camera_pos = LLViewerCamera::getInstance()->getOrigin(); + LLQuaternion new_camera_rot = LLViewerCamera::getInstance()->getQuaternion(); + if (previewp->mForceUpdateSnapshot || + (((gSavedSettings.getBOOL("AutoSnapshot") && LLView::isAvailable(previewp->mViewContainer)) || + (gSavedSettings.getBOOL("FreezeTime") && previewp->mAllowFullScreenPreview)) && + (new_camera_pos != previewp->mCameraPos || dot(new_camera_rot, previewp->mCameraRot) < 0.995f))) + { + previewp->mCameraPos = new_camera_pos; + previewp->mCameraRot = new_camera_rot; + // request a new snapshot whenever the camera moves, with a time delay + bool new_snapshot = gSavedSettings.getBOOL("AutoSnapshot") || previewp->mForceUpdateSnapshot; + LL_DEBUGS("Snapshot") << "camera moved, updating thumbnail" << LL_ENDL; + previewp->updateSnapshot( + new_snapshot, // whether a new snapshot is needed or merely invalidate the existing one + false, // or if 1st arg is false, whether to produce a new thumbnail image. + new_snapshot ? AUTO_SNAPSHOT_TIME_DELAY : 0.f); // shutter delay if 1st arg is true. + previewp->mForceUpdateSnapshot = false; + } + + if (previewp->getSnapshotUpToDate() && previewp->getThumbnailUpToDate()) + { + return false; + } + + // time to produce a snapshot + if(!previewp->getSnapshotUpToDate()) + { + LL_DEBUGS("Snapshot") << "producing snapshot" << LL_ENDL; + if (!previewp->mPreviewImage) + { + previewp->mPreviewImage = new LLImageRaw; + } + + previewp->mSnapshotActive = true; + + previewp->setVisible(false); + previewp->setEnabled(false); + + previewp->getWindow()->incBusyCount(); + previewp->setImageScaled(false); + + // grab the raw image + if (gViewerWindow->rawSnapshot( + previewp->mPreviewImage, + previewp->getWidth(), + previewp->getHeight(), + previewp->mKeepAspectRatio,//gSavedSettings.getBOOL("KeepAspectForSnapshot"), + previewp->getSnapshotType() == LLSnapshotModel::SNAPSHOT_TEXTURE, + previewp->mAllowRenderUI && gSavedSettings.getBOOL("RenderUIInSnapshot"), + gSavedSettings.getBOOL("RenderHUDInSnapshot"), + false, + gSavedSettings.getBOOL("RenderSnapshotNoPost"), + previewp->mSnapshotBufferType, + previewp->getMaxImageSize())) + { + // Invalidate/delete any existing encoded image + previewp->mPreviewImageEncoded = NULL; + // Invalidate/delete any existing formatted image + previewp->mFormattedImage = NULL; + // Update the data size + previewp->estimateDataSize(); + + // Full size preview is set: get the decoded image result and save it for animation + if (gSavedSettings.getBOOL("UseFreezeFrame") && previewp->mAllowFullScreenPreview) + { + previewp->prepareFreezeFrame(); + } + + // The snapshot is updated now... + previewp->mSnapshotUpToDate = true; + + // We need to update the thumbnail though + previewp->setThumbnailImageSize(); + previewp->generateThumbnailImage(true) ; + } + previewp->getWindow()->decBusyCount(); + previewp->setVisible(gSavedSettings.getBOOL("UseFreezeFrame") && previewp->mAllowFullScreenPreview); // only show fullscreen preview when in freeze frame mode + previewp->mSnapshotActive = false; + LL_DEBUGS("Snapshot") << "done creating snapshot" << LL_ENDL; + } + + if (!previewp->getThumbnailUpToDate()) + { + previewp->generateThumbnailImage() ; + } + + // Tell the floater container that the snapshot is updated now + if (previewp->mViewContainer) + { + previewp->mViewContainer->notify(LLSD().with("snapshot-updated", true)); + } + + return true; +} + +void LLSnapshotLivePreview::prepareFreezeFrame() +{ + // Get the decoded version of the formatted image + getEncodedImage(); + + LLImageDataSharedLock lock(mPreviewImageEncoded); + + // We need to scale that a bit for display... + LLPointer scaled = new LLImageRaw( + mPreviewImageEncoded->getData(), + mPreviewImageEncoded->getWidth(), + mPreviewImageEncoded->getHeight(), + mPreviewImageEncoded->getComponents()); + + if (!scaled->isBufferInvalid()) + { + // leave original image dimensions, just scale up texture buffer + if (mPreviewImageEncoded->getWidth() > 1024 || mPreviewImageEncoded->getHeight() > 1024) + { + // go ahead and shrink image to appropriate power of 2 for display + scaled->biasedScaleToPowerOfTwo(1024); + setImageScaled(true); + } + else + { + // expand image but keep original image data intact + scaled->expandToPowerOfTwo(1024, false); + } + + mViewerImage[mCurImageIndex] = LLViewerTextureManager::getLocalTexture(scaled.get(), false); + LLPointer curr_preview_image = mViewerImage[mCurImageIndex]; + gGL.getTexUnit(0)->bind(curr_preview_image); + curr_preview_image->setFilteringOption(getSnapshotType() == LLSnapshotModel::SNAPSHOT_TEXTURE ? LLTexUnit::TFO_ANISOTROPIC : LLTexUnit::TFO_POINT); + curr_preview_image->setAddressMode(LLTexUnit::TAM_CLAMP); + + + if (gSavedSettings.getBOOL("UseFreezeFrame") && mAllowFullScreenPreview) + { + mShineCountdown = 4; // wait a few frames to avoid animation glitch due to readback this frame + } + } +} + +S32 LLSnapshotLivePreview::getEncodedImageWidth() const +{ + S32 width = getWidth(); + if (getSnapshotType() == LLSnapshotModel::SNAPSHOT_TEXTURE) + { + width = LLImageRaw::biasedDimToPowerOfTwo(width,MAX_TEXTURE_SIZE); + } + return width; +} +S32 LLSnapshotLivePreview::getEncodedImageHeight() const +{ + S32 height = getHeight(); + if (getSnapshotType() == LLSnapshotModel::SNAPSHOT_TEXTURE) + { + height = LLImageRaw::biasedDimToPowerOfTwo(height,MAX_TEXTURE_SIZE); + } + return height; +} + +LLPointer LLSnapshotLivePreview::getEncodedImage() +{ + if (!mPreviewImageEncoded) + { + LLImageDataSharedLock lock(mPreviewImage); + + mPreviewImageEncoded = new LLImageRaw; + + mPreviewImageEncoded->resize( + mPreviewImage->getWidth(), + mPreviewImage->getHeight(), + mPreviewImage->getComponents()); + + if (getSnapshotType() == LLSnapshotModel::SNAPSHOT_TEXTURE) + { + // We don't store the intermediate formatted image in mFormattedImage in the J2C case + LL_DEBUGS("Snapshot") << "Encoding new image of format J2C" << LL_ENDL; + LLPointer formatted = new LLImageJ2C; + // Copy the preview + LLPointer scaled = new LLImageRaw( + mPreviewImage->getData(), + mPreviewImage->getWidth(), + mPreviewImage->getHeight(), + mPreviewImage->getComponents()); + // Scale it as required by J2C + scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE); + setImageScaled(true); + // Compress to J2C + if (formatted->encode(scaled, 0.f)) + { + // We can update the data size precisely at that point + mDataSize = formatted->getDataSize(); + // Decompress back + formatted->decode(mPreviewImageEncoded, 0); + } + } + else + { + // Update mFormattedImage if necessary + getFormattedImage(); + if (getSnapshotFormat() == LLSnapshotModel::SNAPSHOT_FORMAT_BMP) + { + // BMP hack : copy instead of decode otherwise decode will crash. + mPreviewImageEncoded->copy(mPreviewImage); + } + else + { + // Decode back + mFormattedImage->decode(mPreviewImageEncoded, 0); + } + } + } + return mPreviewImageEncoded; +} + +bool LLSnapshotLivePreview::createUploadFile(const std::string &out_filename, const S32 max_image_dimentions, const S32 min_image_dimentions) +{ + 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 +// Note : whenever formatted image is computed, mDataSize will be updated to reflect the true size +void LLSnapshotLivePreview::estimateDataSize() +{ + // Compression ratio + F32 ratio = 1.0; + + if (getSnapshotType() == LLSnapshotModel::SNAPSHOT_TEXTURE) + { + ratio = 8.0; // This is what we shoot for when compressing to J2C + } + else + { + LLSnapshotModel::ESnapshotFormat format = getSnapshotFormat(); + switch (format) + { + case LLSnapshotModel::SNAPSHOT_FORMAT_PNG: + ratio = 3.0; // Average observed PNG compression ratio + break; + case LLSnapshotModel::SNAPSHOT_FORMAT_JPEG: + // Observed from JPG compression tests + ratio = (110 - mSnapshotQuality) / 2; + break; + case LLSnapshotModel::SNAPSHOT_FORMAT_BMP: + ratio = 1.0; // No compression with BMP + break; + } + } + mDataSize = (S32)((F32)mPreviewImage->getDataSize() / ratio); +} + +LLPointer LLSnapshotLivePreview::getFormattedImage() +{ + if (!mFormattedImage) + { + // Apply the filter to mPreviewImage + if (getFilter() != "") + { + std::string filter_path = LLImageFiltersManager::getInstance()->getFilterPath(getFilter()); + if (filter_path != "") + { + LLImageFilter filter(filter_path); + filter.executeFilter(mPreviewImage); + } + else + { + LL_WARNS("Snapshot") << "Couldn't find a path to the following filter : " << getFilter() << LL_ENDL; + } + } + + // Create the new formatted image of the appropriate format. + LLSnapshotModel::ESnapshotFormat format = getSnapshotFormat(); + LL_DEBUGS("Snapshot") << "Encoding new image of format " << format << LL_ENDL; + + switch (format) + { + case LLSnapshotModel::SNAPSHOT_FORMAT_PNG: + mFormattedImage = new LLImagePNG(); + break; + case LLSnapshotModel::SNAPSHOT_FORMAT_JPEG: + mFormattedImage = new LLImageJPEG(mSnapshotQuality); + break; + case LLSnapshotModel::SNAPSHOT_FORMAT_BMP: + mFormattedImage = new LLImageBMP(); + break; + } + if (mFormattedImage->encode(mPreviewImage, 0)) + { + // We can update the data size precisely at that point + mDataSize = mFormattedImage->getDataSize(); + } + } + return mFormattedImage; +} + +void LLSnapshotLivePreview::setSize(S32 w, S32 h) +{ + LL_DEBUGS("Snapshot") << "setSize(" << w << ", " << h << ")" << LL_ENDL; + setWidth(w); + setHeight(h); +} + +void LLSnapshotLivePreview::setSnapshotFormat(LLSnapshotModel::ESnapshotFormat format) +{ + if (mSnapshotFormat != format) + { + mFormattedImage = NULL; // Invalidate the already formatted image if any + mSnapshotFormat = format; + } +} + +void LLSnapshotLivePreview::getSize(S32& w, S32& h) const +{ + w = getWidth(); + h = getHeight(); +} + +void LLSnapshotLivePreview::saveTexture(bool outfit_snapshot, std::string name) +{ + LLImageDataSharedLock lock(mPreviewImage); + + LL_DEBUGS("Snapshot") << "saving texture: " << mPreviewImage->getWidth() << "x" << mPreviewImage->getHeight() << LL_ENDL; + // gen a new uuid for this asset + LLTransactionID tid; + tid.generate(); + LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); + + LLPointer formatted = new LLImageJ2C; + LLPointer scaled = new LLImageRaw(mPreviewImage->getData(), + mPreviewImage->getWidth(), + mPreviewImage->getHeight(), + mPreviewImage->getComponents()); + + // Apply the filter to mPreviewImage + if (getFilter() != "") + { + std::string filter_path = LLImageFiltersManager::getInstance()->getFilterPath(getFilter()); + if (filter_path != "") + { + LLImageFilter filter(filter_path); + filter.executeFilter(scaled); + } + else + { + LL_WARNS("Snapshot") << "Couldn't find a path to the following filter : " << getFilter() << LL_ENDL; + } + } + + scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE); + LL_DEBUGS("Snapshot") << "scaled texture to " << scaled->getWidth() << "x" << scaled->getHeight() << LL_ENDL; + + if (formatted->encode(scaled, 0.0f)) + { + LLFileSystem fmt_file(new_asset_id, LLAssetType::AT_TEXTURE, LLFileSystem::WRITE); + fmt_file.write(formatted->getData(), formatted->getDataSize()); + std::string pos_string; + LLAgentUI::buildLocationString(pos_string, LLAgentUI::LOCATION_FORMAT_FULL); + std::string who_took_it; + LLAgentUI::buildFullname(who_took_it); + S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(); + std::string res_name = outfit_snapshot ? name : "Snapshot : " + pos_string; + std::string res_desc = outfit_snapshot ? "" : "Taken by " + who_took_it + " at " + pos_string; + LLFolderType::EType folder_type = outfit_snapshot ? LLFolderType::FT_NONE : LLFolderType::FT_SNAPSHOT_CATEGORY; + LLInventoryType::EType inv_type = outfit_snapshot ? LLInventoryType::IT_NONE : LLInventoryType::IT_SNAPSHOT; + + LLResourceUploadInfo::ptr_t assetUploadInfo(new LLResourceUploadInfo( + tid, LLAssetType::AT_TEXTURE, res_name, res_desc, 0, + folder_type, inv_type, + PERM_ALL, LLFloaterPerms::getGroupPerms("Uploads"), LLFloaterPerms::getEveryonePerms("Uploads"), + expected_upload_cost, !outfit_snapshot)); + + upload_new_resource(assetUploadInfo); + + gViewerWindow->playSnapshotAnimAndSound(); + } + else + { + LLNotificationsUtil::add("ErrorEncodingSnapshot"); + LL_WARNS("Snapshot") << "Error encoding snapshot" << LL_ENDL; + } + + add(LLStatViewer::SNAPSHOT, 1); + + mDataSize = 0; +} + +void LLSnapshotLivePreview::saveLocal(const snapshot_saved_signal_t::slot_type& success_cb, const snapshot_saved_signal_t::slot_type& failure_cb) +{ + // Update mFormattedImage if necessary + getFormattedImage(); + + // Save the formatted image + saveLocal(mFormattedImage, success_cb, failure_cb); +} + +//Check if failed due to insufficient memory +void LLSnapshotLivePreview::saveLocal(LLPointer image, const snapshot_saved_signal_t::slot_type& success_cb, const snapshot_saved_signal_t::slot_type& failure_cb) +{ + sSaveLocalImage = image; + + gViewerWindow->saveImageNumbered(sSaveLocalImage, false, success_cb, failure_cb); +} -- cgit v1.2.3