PageRenderTime 128ms CodeModel.GetById 30ms app.highlight 72ms RepoModel.GetById 18ms app.codeStats 1ms

/indra/newview/llpreviewtexture.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 532 lines | 405 code | 73 blank | 54 comment | 42 complexity | 4f295fe2608c07e5bf729b7a8f1bc7ca MD5 | raw file
  1/** 
  2 * @file llpreviewtexture.cpp
  3 * @brief LLPreviewTexture class implementation
  4 *
  5 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "llviewerprecompiledheaders.h"
 28
 29#include "llwindow.h"
 30
 31#include "llpreviewtexture.h"
 32
 33#include "llagent.h"
 34#include "llbutton.h"
 35#include "llcombobox.h"
 36#include "llfilepicker.h"
 37#include "llfloaterreg.h"
 38#include "llimagetga.h"
 39#include "llinventory.h"
 40#include "llnotificationsutil.h"
 41#include "llresmgr.h"
 42#include "lltrans.h"
 43#include "lltextbox.h"
 44#include "lltextureview.h"
 45#include "llui.h"
 46#include "llviewerinventory.h"
 47#include "llviewertexture.h"
 48#include "llviewertexturelist.h"
 49#include "lluictrlfactory.h"
 50#include "llviewerwindow.h"
 51#include "lllineeditor.h"
 52
 53const S32 CLIENT_RECT_VPAD = 4;
 54
 55const F32 SECONDS_TO_SHOW_FILE_SAVED_MSG = 8.f;
 56
 57const F32 PREVIEW_TEXTURE_MAX_ASPECT = 200.f;
 58const F32 PREVIEW_TEXTURE_MIN_ASPECT = 0.005f;
 59
 60
 61LLPreviewTexture::LLPreviewTexture(const LLSD& key)
 62	: LLPreview(key),
 63	  mLoadingFullImage( FALSE ),
 64	  mShowKeepDiscard(FALSE),
 65	  mCopyToInv(FALSE),
 66	  mIsCopyable(FALSE),
 67	  mUpdateDimensions(TRUE),
 68	  mLastHeight(0),
 69	  mLastWidth(0),
 70	  mAspectRatio(0.f),
 71	  mPreviewToSave(FALSE),
 72	  mImage(NULL),
 73	  mImageOldBoostLevel(LLViewerTexture::BOOST_NONE)
 74{
 75	updateImageID();
 76	if (key.has("save_as"))
 77	{
 78		mPreviewToSave = TRUE;
 79	}
 80}
 81
 82LLPreviewTexture::~LLPreviewTexture()
 83{
 84	LLLoadedCallbackEntry::cleanUpCallbackList(&mCallbackTextureList) ;
 85
 86	if( mLoadingFullImage )
 87	{
 88		getWindow()->decBusyCount();
 89	}
 90	mImage->setBoostLevel(mImageOldBoostLevel);
 91	mImage = NULL;
 92}
 93
 94// virtual
 95BOOL LLPreviewTexture::postBuild()
 96{
 97	if (mCopyToInv) 
 98	{
 99		getChild<LLButton>("Keep")->setLabel(getString("Copy"));
100		childSetAction("Keep",LLPreview::onBtnCopyToInv,this);
101		getChildView("Discard")->setVisible( false);
102	}
103	else if (mShowKeepDiscard)
104	{
105		childSetAction("Keep",onKeepBtn,this);
106		childSetAction("Discard",onDiscardBtn,this);
107	}
108	else
109	{
110		getChildView("Keep")->setVisible( false);
111		getChildView("Discard")->setVisible( false);
112	}
113	
114	childSetAction("save_tex_btn", LLPreviewTexture::onSaveAsBtn, this);
115	getChildView("save_tex_btn")->setVisible( true);
116	getChildView("save_tex_btn")->setEnabled(canSaveAs());
117	
118	if (!mCopyToInv) 
119	{
120		const LLInventoryItem* item = getItem();
121		
122		if (item)
123		{
124			childSetCommitCallback("desc", LLPreview::onText, this);
125			getChild<LLUICtrl>("desc")->setValue(item->getDescription());
126			getChild<LLLineEditor>("desc")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe);
127		}
128	}
129	
130	childSetCommitCallback("combo_aspect_ratio", onAspectRatioCommit, this);
131	LLComboBox* combo = getChild<LLComboBox>("combo_aspect_ratio");
132	combo->setCurrentByIndex(0);
133	
134	return LLPreview::postBuild();
135}
136
137// static
138void LLPreviewTexture::onSaveAsBtn(void* data)
139{
140	LLPreviewTexture* self = (LLPreviewTexture*)data;
141	self->saveAs();
142}
143
144void LLPreviewTexture::draw()
145{
146	updateDimensions();
147	
148	LLPreview::draw();
149
150	if (!isMinimized())
151	{
152		LLGLSUIDefault gls_ui;
153		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
154		
155		const LLRect& border = mClientRect;
156		LLRect interior = mClientRect;
157		interior.stretch( -PREVIEW_BORDER_WIDTH );
158
159		// ...border
160		gl_rect_2d( border, LLColor4(0.f, 0.f, 0.f, 1.f));
161		gl_rect_2d_checkerboard( interior );
162
163		if ( mImage.notNull() )
164		{
165			// Automatically bring up SaveAs dialog if we opened this to save the texture.
166			if (mPreviewToSave)
167			{
168				mPreviewToSave = FALSE;
169				saveAs();
170			}
171			// Draw the texture
172			gGL.diffuseColor3f( 1.f, 1.f, 1.f );
173			gl_draw_scaled_image(interior.mLeft,
174								interior.mBottom,
175								interior.getWidth(),
176								interior.getHeight(),
177								mImage);
178
179			// Pump the texture priority
180			F32 pixel_area = mLoadingFullImage ? (F32)MAX_IMAGE_AREA  : (F32)(interior.getWidth() * interior.getHeight() );
181			mImage->addTextureStats( pixel_area );
182
183			// Don't bother decoding more than we can display, unless
184			// we're loading the full image.
185			if (!mLoadingFullImage)
186			{
187				S32 int_width = interior.getWidth();
188				S32 int_height = interior.getHeight();
189				mImage->setKnownDrawSize(int_width, int_height);
190			}
191			else
192			{
193				// Don't use this feature
194				mImage->setKnownDrawSize(0, 0);
195			}
196
197			if( mLoadingFullImage )
198			{
199				LLFontGL::getFontSansSerif()->renderUTF8(LLTrans::getString("Receiving"), 0,
200					interior.mLeft + 4, 
201					interior.mBottom + 4,
202					LLColor4::white, LLFontGL::LEFT, LLFontGL::BOTTOM,
203					LLFontGL::NORMAL,
204					LLFontGL::DROP_SHADOW);
205				
206				F32 data_progress = mImage->getDownloadProgress() ;
207				
208				// Draw the progress bar.
209				const S32 BAR_HEIGHT = 12;
210				const S32 BAR_LEFT_PAD = 80;
211				S32 left = interior.mLeft + 4 + BAR_LEFT_PAD;
212				S32 bar_width = getRect().getWidth() - left - RESIZE_HANDLE_WIDTH - 2;
213				S32 top = interior.mBottom + 4 + BAR_HEIGHT;
214				S32 right = left + bar_width;
215				S32 bottom = top - BAR_HEIGHT;
216
217				LLColor4 background_color(0.f, 0.f, 0.f, 0.75f);
218				LLColor4 decoded_color(0.f, 1.f, 0.f, 1.0f);
219				LLColor4 downloaded_color(0.f, 0.5f, 0.f, 1.0f);
220
221				gl_rect_2d(left, top, right, bottom, background_color);
222
223				if (data_progress > 0.0f)
224				{
225					// Downloaded bytes
226					right = left + llfloor(data_progress * (F32)bar_width);
227					if (right > left)
228					{
229						gl_rect_2d(left, top, right, bottom, downloaded_color);
230					}
231				}
232			}
233			else
234			if( !mSavedFileTimer.hasExpired() )
235			{
236				LLFontGL::getFontSansSerif()->renderUTF8(LLTrans::getString("FileSaved"), 0,
237					interior.mLeft + 4,
238					interior.mBottom + 4,
239					LLColor4::white, LLFontGL::LEFT, LLFontGL::BOTTOM,
240					LLFontGL::NORMAL,
241					LLFontGL::DROP_SHADOW);
242			}
243		}
244	} 
245
246}
247
248
249// virtual
250BOOL LLPreviewTexture::canSaveAs() const
251{
252	return mIsCopyable && !mLoadingFullImage && mImage.notNull() && !mImage->isMissingAsset();
253}
254
255
256// virtual
257void LLPreviewTexture::saveAs()
258{
259	if( mLoadingFullImage )
260		return;
261
262	LLFilePicker& file_picker = LLFilePicker::instance();
263	const LLInventoryItem* item = getItem() ;
264	if( !file_picker.getSaveFile( LLFilePicker::FFSAVE_TGA, item ? LLDir::getScrubbedFileName(item->getName()) : LLStringUtil::null) )
265	{
266		// User canceled or we failed to acquire save file.
267		return;
268	}
269	// remember the user-approved/edited file name.
270	mSaveFileName = file_picker.getFirstFile();
271	mLoadingFullImage = TRUE;
272	getWindow()->incBusyCount();
273
274	mImage->forceToSaveRawImage(0) ;//re-fetch the raw image if the old one is removed.
275	mImage->setLoadedCallback( LLPreviewTexture::onFileLoadedForSave, 
276								0, TRUE, FALSE, new LLUUID( mItemUUID ), &mCallbackTextureList );
277}
278
279// virtual
280void LLPreviewTexture::reshape(S32 width, S32 height, BOOL called_from_parent)
281{
282	LLPreview::reshape(width, height, called_from_parent);
283
284	LLRect dim_rect(getChildView("dimensions")->getRect());
285
286	S32 horiz_pad = 2 * (LLPANEL_BORDER_WIDTH + PREVIEW_PAD) + PREVIEW_RESIZE_HANDLE_SIZE;
287
288	// add space for dimensions and aspect ratio
289	S32 info_height = dim_rect.mTop + CLIENT_RECT_VPAD;
290
291	LLRect client_rect(horiz_pad, getRect().getHeight(), getRect().getWidth() - horiz_pad, 0);
292	client_rect.mTop -= (PREVIEW_HEADER_SIZE + CLIENT_RECT_VPAD);
293	client_rect.mBottom += PREVIEW_BORDER + CLIENT_RECT_VPAD + info_height ;
294
295	S32 client_width = client_rect.getWidth();
296	S32 client_height = client_rect.getHeight();
297
298	if (mAspectRatio > 0.f)
299	{
300		if(mAspectRatio > 1.f)
301		{
302			client_height = llceil((F32)client_width / mAspectRatio);
303			if(client_height > client_rect.getHeight())
304			{
305				client_height = client_rect.getHeight();
306				client_width = llceil((F32)client_height * mAspectRatio);
307			}
308		}
309		else//mAspectRatio < 1.f
310		{
311			client_width = llceil((F32)client_height * mAspectRatio);
312			if(client_width > client_rect.getWidth())
313			{
314				client_width = client_rect.getWidth();
315				client_height = llceil((F32)client_width / mAspectRatio);
316			}
317		}
318	}
319
320	mClientRect.setLeftTopAndSize(client_rect.getCenterX() - (client_width / 2), client_rect.getCenterY() +  (client_height / 2), client_width, client_height);
321
322}
323
324// virtual
325void LLPreviewTexture::onFocusReceived()
326{
327	LLPreview::onFocusReceived();
328}
329
330void LLPreviewTexture::openToSave()
331{
332	mPreviewToSave = TRUE;
333}
334
335// static
336void LLPreviewTexture::onFileLoadedForSave(BOOL success, 
337					   LLViewerFetchedTexture *src_vi,
338					   LLImageRaw* src, 
339					   LLImageRaw* aux_src, 
340					   S32 discard_level,
341					   BOOL final,
342					   void* userdata)
343{
344	LLUUID* item_uuid = (LLUUID*) userdata;
345
346	LLPreviewTexture* self = LLFloaterReg::findTypedInstance<LLPreviewTexture>("preview_texture", *item_uuid);
347
348	if( final || !success )
349	{
350		delete item_uuid;
351
352		if( self )
353		{
354			self->getWindow()->decBusyCount();
355			self->mLoadingFullImage = FALSE;
356		}
357	}
358
359	if( self && final && success )
360	{
361		LLPointer<LLImageTGA> image_tga = new LLImageTGA;
362		if( !image_tga->encode( src ) )
363		{
364			LLSD args;
365			args["FILE"] = self->mSaveFileName;
366			LLNotificationsUtil::add("CannotEncodeFile", args);
367		}
368		else if( !image_tga->save( self->mSaveFileName ) )
369		{
370			LLSD args;
371			args["FILE"] = self->mSaveFileName;
372			LLNotificationsUtil::add("CannotWriteFile", args);
373		}
374		else
375		{
376			self->mSavedFileTimer.reset();
377			self->mSavedFileTimer.setTimerExpirySec( SECONDS_TO_SHOW_FILE_SAVED_MSG );
378		}
379
380		self->mSaveFileName.clear();
381	}
382
383	if( self && !success )
384	{
385		LLNotificationsUtil::add("CannotDownloadFile");
386	}
387
388}
389
390
391// It takes a while until we get height and width information.
392// When we receive it, reshape the window accordingly.
393void LLPreviewTexture::updateDimensions()
394{
395	if (!mImage)
396	{
397		return;
398	}
399	if ((mImage->getFullWidth() * mImage->getFullHeight()) == 0)
400	{
401		return;
402	}
403	
404	// Update the width/height display every time
405	getChild<LLUICtrl>("dimensions")->setTextArg("[WIDTH]",  llformat("%d", mImage->getFullWidth()));
406	getChild<LLUICtrl>("dimensions")->setTextArg("[HEIGHT]", llformat("%d", mImage->getFullHeight()));
407
408	// Reshape the floater only when required
409	if (mUpdateDimensions)
410	{
411		mUpdateDimensions = FALSE;
412		
413		//reshape floater
414		reshape(getRect().getWidth(), getRect().getHeight());
415
416		gFloaterView->adjustToFitScreen(this, FALSE);
417
418		LLRect dim_rect(getChildView("dimensions")->getRect());
419		LLRect aspect_label_rect(getChildView("aspect_ratio")->getRect());
420		getChildView("aspect_ratio")->setVisible( dim_rect.mRight < aspect_label_rect.mLeft);
421	}
422}
423
424
425// Return true if everything went fine, false if we somewhat modified the ratio as we bumped on border values
426bool LLPreviewTexture::setAspectRatio(const F32 width, const F32 height)
427{
428	mUpdateDimensions = TRUE;
429
430	// We don't allow negative width or height. Also, if height is positive but too small, we reset to default
431	// A default 0.f value for mAspectRatio means "unconstrained" in the rest of the code
432	if ((width <= 0.f) || (height <= F_APPROXIMATELY_ZERO))
433	{
434		mAspectRatio = 0.f;
435		return false;
436	}
437	
438	// Compute and store the ratio
439	F32 ratio = width / height;
440	mAspectRatio = llclamp(ratio, PREVIEW_TEXTURE_MIN_ASPECT, PREVIEW_TEXTURE_MAX_ASPECT);
441	
442	// Return false if we clamped the value, true otherwise
443	return (ratio == mAspectRatio);
444}
445
446
447void LLPreviewTexture::onAspectRatioCommit(LLUICtrl* ctrl, void* userdata)
448{	
449	LLPreviewTexture* self = (LLPreviewTexture*) userdata;
450	
451	std::string ratio(ctrl->getValue().asString());
452	std::string::size_type separator(ratio.find_first_of(":/\\"));
453	
454	if (std::string::npos == separator) {
455		// If there's no separator assume we want an unconstrained ratio
456		self->setAspectRatio( 0.f, 0.f );
457		return;
458	}
459	
460	F32 width, height;
461	std::istringstream numerator(ratio.substr(0, separator));
462	std::istringstream denominator(ratio.substr(separator + 1));
463	numerator >> width;
464	denominator >> height;
465	
466	self->setAspectRatio( width, height );	
467}
468
469void LLPreviewTexture::loadAsset()
470{
471	mImage = LLViewerTextureManager::getFetchedTexture(mImageID, MIPMAP_TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
472	mImageOldBoostLevel = mImage->getBoostLevel();
473	mImage->setBoostLevel(LLViewerTexture::BOOST_PREVIEW);
474	mImage->forceToSaveRawImage(0) ;
475	mAssetStatus = PREVIEW_ASSET_LOADING;
476	mUpdateDimensions = TRUE;
477	updateDimensions();
478	getChildView("save_tex_btn")->setEnabled(canSaveAs());
479}
480
481LLPreview::EAssetStatus LLPreviewTexture::getAssetStatus()
482{
483	if (mImage.notNull() && (mImage->getFullWidth() * mImage->getFullHeight() > 0))
484	{
485		mAssetStatus = PREVIEW_ASSET_LOADED;
486	}
487	return mAssetStatus;
488}
489
490void LLPreviewTexture::updateImageID()
491{
492	const LLViewerInventoryItem *item = static_cast<const LLViewerInventoryItem*>(getItem());
493	if(item)
494	{
495		mImageID = item->getAssetUUID();
496
497		// here's the old logic...
498		//mShowKeepDiscard = item->getPermissions().getCreator() != gAgent.getID();
499		// here's the new logic... 'cos we hate disappearing buttons.
500		mShowKeepDiscard = TRUE;
501
502		mCopyToInv = FALSE;
503		mIsCopyable = item->checkPermissionsSet(PERM_ITEM_UNRESTRICTED);
504	}
505	else // not an item, assume it's an asset id
506	{
507		mImageID = mItemUUID;
508		mShowKeepDiscard = FALSE;
509		mCopyToInv = TRUE;
510		mIsCopyable = TRUE;
511	}
512
513}
514
515/* virtual */
516void LLPreviewTexture::setObjectID(const LLUUID& object_id)
517{
518	mObjectUUID = object_id;
519
520	const LLUUID old_image_id = mImageID;
521
522	// Update what image we're pointing to, such as if we just specified the mObjectID
523	// that this mItemID is part of.
524	updateImageID();
525
526	// If the imageID has changed, start over and reload the new image.
527	if (mImageID != old_image_id)
528	{
529		mAssetStatus = PREVIEW_ASSET_UNLOADED;
530		loadAsset();
531	}
532}