implment bitmap browser. Work done by Vaalith Jinn for STORM-64.

master
Tank_Master 2011-09-04 11:13:47 -07:00
parent 5ae11b0677
commit 640191a3ad
11 changed files with 1266 additions and 201 deletions

View File

@ -872,6 +872,8 @@ Twisted Laws
STORM-1103
Vadim Bigbear
VWR-2681
Vaalith Jinn
STORM-64
Vector Hastings
VWR-8726
Vixen Heron

View File

@ -320,6 +320,7 @@ set(viewer_SOURCE_FILES
lllistbrowser.cpp
lllistcontextmenu.cpp
lllistview.cpp
lllocalbitmaps.cpp
lllocaltextureobject.cpp
lllocationhistory.cpp
lllocationinputctrl.cpp
@ -921,6 +922,7 @@ set(viewer_HEADER_FILES
lllistbrowser.h
lllistcontextmenu.h
lllistview.h
lllocalbitmaps.h
lllocaltextureobject.h
lllocationhistory.h
lllocationinputctrl.h

View File

@ -0,0 +1,830 @@
/**
* @file lllocalbitmaps.cpp
* @author Vaalith Jinn
* @brief Local Bitmap Browser source
*
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2011, 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$
*/
/* precompiled headers */
#include "llviewerprecompiledheaders.h"
/* own header */
#include "lllocalbitmaps.h"
/* boost: in case of boost filesystem related crashes check ifdef 'equivalent', if true undef it. */
#include <boost/filesystem.hpp>
/* image compression headers. */
#include "llimagebmp.h"
#include "llimagetga.h"
#include "llimagejpeg.h"
#include "llimagepng.h"
/* time headers */
#include <time.h>
#include <ctime>
/* misc headers */
#include "llscrolllistctrl.h"
#include "llfilepicker.h"
#include "llviewertexturelist.h"
#include "llviewerobjectlist.h"
#include "llviewerobject.h"
#include "llface.h"
#include "llvoavatarself.h"
#include "llwearable.h"
#include "llagentwearables.h"
#include "lltexlayerparams.h"
/*=======================================*/
/* Formal declarations, constants, etc. */
/*=======================================*/
std::list<LLLocalBitmap*> LLLocalBitmapMgr::sBitmapList;
LLLocalBitmapTimer LLLocalBitmapMgr::sTimer;
bool LLLocalBitmapMgr::sNeedsRebake;
static const F32 LL_LOCAL_TIMER_HEARTBEAT = 3.0;
static const BOOL LL_LOCAL_USE_MIPMAPS = true;
static const S32 LL_LOCAL_DISCARD_LEVEL = 0;
static const U32 LL_LOCAL_TEXLAYER_FOR_IDX = 0;
static const bool LL_LOCAL_SLAM_FOR_DEBUG = true;
static const bool LL_LOCAL_REPLACE_ON_DEL = true;
static const S32 LL_LOCAL_UPDATE_RETRIES = 5;
/*=======================================*/
/* LLLocalBitmap: unit class */
/*=======================================*/
LLLocalBitmap::LLLocalBitmap(std::string filename)
{
/* basic properties */
mFilename = filename;
mShortName = gDirUtilp->getBaseFileName(filename, true);
mValid = false;
mLastModified = NULL;
mLinkStatus = LS_ON;
mUpdateRetries = LL_LOCAL_UPDATE_RETRIES;
mTrackingID.generate();
/* extension */
std::string temp_exten = gDirUtilp->getExtension(mFilename);
if (temp_exten == "bmp")
{
mExtension = ET_IMG_BMP;
}
else if (temp_exten == "tga")
{
mExtension = ET_IMG_TGA;
}
else if (temp_exten == "jpg" || temp_exten == "jpeg")
{
mExtension = ET_IMG_JPG;
}
else if (temp_exten == "png")
{
mExtension = ET_IMG_PNG;
}
else
{
return; // no valid extension.
}
/* next phase of unit creation is nearly the same as an update cycle.
true means the unit's update is running for the first time so it will not check
for current usage nor will it attempt to replace the old, non existent image */
mValid = updateSelf(true);
}
LLLocalBitmap::~LLLocalBitmap()
{
// replace IDs with defaults, if set to do so.
if(LL_LOCAL_REPLACE_ON_DEL)
{
replaceIDs(mWorldID, IMG_DEFAULT);
LLLocalBitmapMgr::doRebake();
}
// delete self from gimagelist
LLViewerFetchedTexture* image = gTextureList.findImage(mWorldID);
gTextureList.deleteImage(image);
if (image)
{
image->unref();
}
}
/* accessors */
std::string LLLocalBitmap::getFilename()
{
return mFilename;
}
std::string LLLocalBitmap::getShortName()
{
return mShortName;
}
LLUUID LLLocalBitmap::getTrackingID()
{
return mTrackingID;
}
LLUUID LLLocalBitmap::getWorldID()
{
return mWorldID;
}
bool LLLocalBitmap::getValid()
{
return mValid;
}
/* update functions */
bool LLLocalBitmap::updateSelf(bool first_update)
{
if (mLinkStatus == LS_ON)
{
// verifying that the file exists
if (!gDirUtilp->fileExists(mFilename))
{
mLinkStatus = LS_BROKEN;
return false;
}
// verifying that the file has indeed been modified
const std::time_t temp_time = boost::filesystem::last_write_time(boost::filesystem::path(mFilename));
LLSD new_last_modified = asctime(localtime(&temp_time));
if (mLastModified.asString() == new_last_modified.asString())
{
return false;
}
/* loading the image file and decoding it, here is a critical point which,
if fails, invalidates the whole update (or unit creation) process. */
LLPointer<LLImageRaw> raw_image = new LLImageRaw();
if (decodeBitmap(raw_image))
{
// decode is successful, we can safely proceed.
LLUUID old_id = LLUUID::null;
if (!first_update && !mWorldID.isNull())
{
old_id = mWorldID;
}
mWorldID.generate();
mLastModified = new_last_modified;
LLPointer<LLViewerFetchedTexture> texture = new LLViewerFetchedTexture
("file://"+mFilename, mWorldID, LL_LOCAL_USE_MIPMAPS);
texture->createGLTexture(LL_LOCAL_DISCARD_LEVEL, raw_image);
texture->setCachedRawImage(LL_LOCAL_DISCARD_LEVEL, raw_image);
texture->ref();
gTextureList.addImage(texture);
if (!first_update)
{
// seek out everything old_id uses and replace it with mWorldID
replaceIDs(old_id, mWorldID);
// remove old_id from gimagelist
LLViewerFetchedTexture* image = gTextureList.findImage(old_id);
gTextureList.deleteImage(image);
image->unref();
}
return true;
}
else
{
if (mUpdateRetries)
{
mUpdateRetries--;
}
else
{
mLinkStatus = LS_BROKEN;
}
}
}
return false;
}
bool LLLocalBitmap::decodeBitmap(LLPointer<LLImageRaw> rawimg)
{
switch (mExtension)
{
case ET_IMG_BMP:
{
LLPointer<LLImageBMP> bmp_image = new LLImageBMP;
if (bmp_image->load(mFilename) && bmp_image->decode(rawimg, 0.0f))
{
rawimg->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
return true;
}
}
case ET_IMG_TGA:
{
LLPointer<LLImageTGA> tga_image = new LLImageTGA;
if ((tga_image->load(mFilename) && tga_image->decode(rawimg))
&& ((tga_image->getComponents() == 3) || (tga_image->getComponents() == 4)))
{
rawimg->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
return true;
}
}
case ET_IMG_JPG:
{
LLPointer<LLImageJPEG> jpeg_image = new LLImageJPEG;
if (jpeg_image->load(mFilename) && jpeg_image->decode(rawimg, 0.0f))
{
rawimg->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
return true;
}
}
case ET_IMG_PNG:
{
LLPointer<LLImagePNG> png_image = new LLImagePNG;
if (png_image->load(mFilename) && png_image->decode(rawimg, 0.0f))
{
rawimg->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
return true;
}
}
default:
{
}
}
return false;
}
void LLLocalBitmap::replaceIDs(LLUUID old_id, LLUUID new_id)
{
// checking for misuse.
if (old_id == new_id)
{
return;
}
updateUserPrims(old_id, new_id);
updateUserSculpts(old_id, new_id); // isn't there supposed to be an IMG_DEFAULT_SCULPT or something?
// default safeguard image for layers
if( new_id == IMG_DEFAULT )
{
new_id = IMG_DEFAULT_AVATAR;
}
/* It doesn't actually update all of those, it merely checks if any of them
contain the referenced ID and if so, updates. */
updateUserLayers(old_id, new_id, LLWearableType::WT_ALPHA);
updateUserLayers(old_id, new_id, LLWearableType::WT_EYES);
updateUserLayers(old_id, new_id, LLWearableType::WT_GLOVES);
updateUserLayers(old_id, new_id, LLWearableType::WT_JACKET);
updateUserLayers(old_id, new_id, LLWearableType::WT_PANTS);
updateUserLayers(old_id, new_id, LLWearableType::WT_SHIRT);
updateUserLayers(old_id, new_id, LLWearableType::WT_SHOES);
updateUserLayers(old_id, new_id, LLWearableType::WT_SKIN);
updateUserLayers(old_id, new_id, LLWearableType::WT_SKIRT);
updateUserLayers(old_id, new_id, LLWearableType::WT_SOCKS);
updateUserLayers(old_id, new_id, LLWearableType::WT_TATTOO);
updateUserLayers(old_id, new_id, LLWearableType::WT_UNDERPANTS);
updateUserLayers(old_id, new_id, LLWearableType::WT_UNDERSHIRT);
}
void LLLocalBitmap::updateUserPrims(LLUUID old_id, LLUUID new_id)
{
S32 object_count = gObjectList.getNumObjects();
for(S32 object_iter = 0; object_iter < object_count; object_iter++)
{
LLViewerObject* object = gObjectList.getObject(object_iter);
if(object)
{
bool update_obj = false;
S32 num_faces = object->getNumFaces();
for (U8 face_iter = 0; face_iter < num_faces; face_iter++)
{
if (object->mDrawable)
{
LLFace* face = object->mDrawable->getFace(face_iter);
if (face && face->getTexture() && face->getTexture()->getID() == old_id)
{
object->setTEImage(face_iter, LLViewerTextureManager::getFetchedTexture
(new_id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE));
update_obj = true;
}
}
}
if (update_obj)
{
object->sendTEUpdate();
}
}
}
}
void LLLocalBitmap::updateUserSculpts(LLUUID old_id, LLUUID new_id)
{
/* i tried using the volume list that's kept by the image, but vovolume/volume both refuse
to give me a obj to act on the way LLPanelObject::sendSculpt does so.. off to iterating we go. */
S32 object_count = gObjectList.getNumObjects();
for(S32 object_iter = 0; object_iter < object_count; object_iter++)
{
LLViewerObject* object = gObjectList.getObject(object_iter);
if(object)
{
if (object->isSculpted() && object->getVolume() &&
object->getVolume()->getParams().getSculptID() == old_id)
{
LLSculptParams* old_params = (LLSculptParams*)object->getParameterEntry(LLNetworkData::PARAMS_SCULPT);
LLSculptParams new_params(*old_params);
new_params.setSculptTexture(new_id);
object->setParameterEntry(LLNetworkData::PARAMS_SCULPT, new_params, TRUE);
}
}
}
}
void LLLocalBitmap::updateUserLayers(LLUUID old_id, LLUUID new_id, LLWearableType::EType type)
{
U32 count = gAgentWearables.getWearableCount(type);
for(U32 wearable_iter = 0; wearable_iter < count; wearable_iter++)
{
LLWearable* wearable = gAgentWearables.getWearable(type, wearable_iter);
if (!wearable)
{
return; // really shouldn't happen.
}
std::vector<LLLocalTextureObject*> texture_list = wearable->getLocalTextureListSeq();
for(std::vector<LLLocalTextureObject*>::iterator texture_iter = texture_list.begin();
texture_iter != texture_list.end(); texture_iter++)
{
LLLocalTextureObject* lto = *texture_iter;
if (lto && lto->getID() == old_id)
{
U32 local_texlayer_index = 0; /* can't keep that as static const, gives errors, so i'm leaving this var here */
LLVOAvatarDefines::EBakedTextureIndex baked_texind =
lto->getTexLayer(local_texlayer_index)->getTexLayerSet()->getBakedTexIndex();
LLVOAvatarDefines::ETextureIndex reg_texind = getTexIndex(type, baked_texind);
if (reg_texind == LLVOAvatarDefines::TEX_NUM_INDICES) // we have an error
{
return; // do nothing.
}
U32 index = gAgentWearables.getWearableIndex(wearable);
gAgentAvatarp->setLocalTexture(reg_texind, gTextureList.getImage(new_id), FALSE, index);
gAgentAvatarp->wearableUpdated(type, FALSE);
/* telling the manager to rebake once update cycle is fully done */
LLLocalBitmapMgr::setNeedsRebake();
}
}
}
}
LLVOAvatarDefines::ETextureIndex LLLocalBitmap::getTexIndex(
LLWearableType::EType type, LLVOAvatarDefines::EBakedTextureIndex baked_texind)
{
LLVOAvatarDefines::ETextureIndex result = LLVOAvatarDefines::TEX_NUM_INDICES; // using as a default/fail return.
switch(type)
{
case LLWearableType::WT_ALPHA:
{
switch(baked_texind)
{
case LLVOAvatarDefines::BAKED_EYES:
{
result = LLVOAvatarDefines::TEX_EYES_ALPHA;
return result;
}
case LLVOAvatarDefines::BAKED_HAIR:
{
result = LLVOAvatarDefines::TEX_HAIR_ALPHA;
return result;
}
case LLVOAvatarDefines::BAKED_HEAD:
{
result = LLVOAvatarDefines::TEX_HEAD_ALPHA;
return result;
}
case LLVOAvatarDefines::BAKED_LOWER:
{
result = LLVOAvatarDefines::TEX_LOWER_ALPHA;
return result;
}
case LLVOAvatarDefines::BAKED_UPPER:
{
result = LLVOAvatarDefines::TEX_UPPER_ALPHA;
return result;
}
default:
{
return result;
}
}
}
case LLWearableType::WT_EYES:
{
if (baked_texind == LLVOAvatarDefines::BAKED_EYES)
{
result = LLVOAvatarDefines::TEX_EYES_IRIS;
}
return result;
}
case LLWearableType::WT_GLOVES:
{
if (baked_texind == LLVOAvatarDefines::BAKED_UPPER)
{
result = LLVOAvatarDefines::TEX_UPPER_GLOVES;
}
return result;
}
case LLWearableType::WT_JACKET:
{
if (baked_texind == LLVOAvatarDefines::BAKED_LOWER)
{
result = LLVOAvatarDefines::TEX_LOWER_JACKET;
}
else if (baked_texind == LLVOAvatarDefines::BAKED_UPPER)
{
result = LLVOAvatarDefines::TEX_UPPER_JACKET;
}
return result;
}
case LLWearableType::WT_PANTS:
{
if (baked_texind == LLVOAvatarDefines::BAKED_LOWER)
{
result = LLVOAvatarDefines::TEX_LOWER_PANTS;
}
return result;
}
case LLWearableType::WT_SHIRT:
{
if (baked_texind == LLVOAvatarDefines::BAKED_UPPER)
{
result = LLVOAvatarDefines::TEX_UPPER_SHIRT;
}
return result;
}
case LLWearableType::WT_SHOES:
{
if (baked_texind == LLVOAvatarDefines::BAKED_LOWER)
{
result = LLVOAvatarDefines::TEX_LOWER_SHOES;
}
return result;
}
case LLWearableType::WT_SKIN:
{
switch(baked_texind)
{
case LLVOAvatarDefines::BAKED_HEAD:
{
result = LLVOAvatarDefines::TEX_HEAD_BODYPAINT;
return result;
}
case LLVOAvatarDefines::BAKED_LOWER:
{
result = LLVOAvatarDefines::TEX_LOWER_BODYPAINT;
return result;
}
case LLVOAvatarDefines::BAKED_UPPER:
{
result = LLVOAvatarDefines::TEX_UPPER_BODYPAINT;
return result;
}
default:
{
return result;
}
}
}
case LLWearableType::WT_SKIRT:
{
if (baked_texind == LLVOAvatarDefines::BAKED_SKIRT)
{
result = LLVOAvatarDefines::TEX_SKIRT;
}
return result;
}
case LLWearableType::WT_SOCKS:
{
if (baked_texind == LLVOAvatarDefines::BAKED_LOWER)
{
result = LLVOAvatarDefines::TEX_LOWER_SOCKS;
}
return result;
}
case LLWearableType::WT_TATTOO:
{
switch(baked_texind)
{
case LLVOAvatarDefines::BAKED_HEAD:
{
result = LLVOAvatarDefines::TEX_HEAD_TATTOO;
return result;
}
case LLVOAvatarDefines::BAKED_LOWER:
{
result = LLVOAvatarDefines::TEX_LOWER_TATTOO;
return result;
}
case LLVOAvatarDefines::BAKED_UPPER:
{
result = LLVOAvatarDefines::TEX_UPPER_TATTOO;
return result;
}
default:
{
return result;
}
}
}
case LLWearableType::WT_UNDERPANTS:
{
if (baked_texind == LLVOAvatarDefines::BAKED_LOWER)
{
result = LLVOAvatarDefines::TEX_LOWER_UNDERPANTS;
}
return result;
}
case LLWearableType::WT_UNDERSHIRT:
{
if (baked_texind == LLVOAvatarDefines::BAKED_UPPER)
{
result = LLVOAvatarDefines::TEX_UPPER_UNDERSHIRT;
}
return result;
}
default:
{
return result;
}
}
}
/*=======================================*/
/* LLLocalBitmapTimer: timer class */
/*=======================================*/
LLLocalBitmapTimer::LLLocalBitmapTimer() : LLEventTimer(LL_LOCAL_TIMER_HEARTBEAT)
{
}
LLLocalBitmapTimer::~LLLocalBitmapTimer()
{
}
void LLLocalBitmapTimer::startTimer()
{
mEventTimer.start();
}
void LLLocalBitmapTimer::stopTimer()
{
mEventTimer.stop();
}
bool LLLocalBitmapTimer::isRunning()
{
return mEventTimer.getStarted();
}
BOOL LLLocalBitmapTimer::tick()
{
LLLocalBitmapMgr::doUpdates();
return FALSE;
}
/*=======================================*/
/* LLLocalBitmapMgr: manager class */
/*=======================================*/
LLLocalBitmapMgr::LLLocalBitmapMgr()
{
// The class is all made of static members, should i even bother instantiating?
}
LLLocalBitmapMgr::~LLLocalBitmapMgr()
{
}
bool LLLocalBitmapMgr::addUnit()
{
bool add_successful = false;
LLFilePicker& picker = LLFilePicker::instance();
if (picker.getMultipleOpenFiles(LLFilePicker::FFLOAD_IMAGE))
{
sTimer.stopTimer();
std::string filename = picker.getFirstFile();
while(!filename.empty())
{
LLLocalBitmap* unit = new LLLocalBitmap(filename);
if (unit->getValid())
{
sBitmapList.push_back(unit);
add_successful = true;
}
else
{
delete unit;
unit = NULL;
}
filename = picker.getNextFile();
}
sTimer.startTimer();
}
return add_successful;
}
void LLLocalBitmapMgr::delUnit(LLUUID tracking_id)
{
if (sBitmapList.empty())
{
return; // in case of misuse.
}
std::vector<LLLocalBitmap*> to_delete;
for (local_list_iter iter = sBitmapList.begin(); iter != sBitmapList.end(); iter++)
{ /* finding which ones we want deleted and making a separate list */
LLLocalBitmap* unit = *iter;
if (unit->getTrackingID() == tracking_id)
{
to_delete.push_back(unit);
}
}
for(std::vector<LLLocalBitmap*>::iterator del_iter = to_delete.begin();
del_iter != to_delete.end(); del_iter++)
{ /* iterating over a temporary list, hence preserving the iterator validity while deleting. */
LLLocalBitmap* unit = *del_iter;
sBitmapList.remove(unit);
delete unit;
unit = NULL;
}
}
LLUUID LLLocalBitmapMgr::getWorldID(LLUUID tracking_id)
{
LLUUID world_id = LLUUID::null;
for (local_list_iter iter = sBitmapList.begin(); iter != sBitmapList.end(); iter++)
{
LLLocalBitmap* unit = *iter;
if (unit->getTrackingID() == tracking_id)
{
world_id = unit->getWorldID();
}
}
return world_id;
}
std::string LLLocalBitmapMgr::getFilename(LLUUID tracking_id)
{
std::string filename = "";
for (local_list_iter iter = sBitmapList.begin(); iter != sBitmapList.end(); iter++)
{
LLLocalBitmap* unit = *iter;
if (unit->getTrackingID() == tracking_id)
{
filename = unit->getFilename();
}
}
return filename;
}
void LLLocalBitmapMgr::feedScrollList(LLScrollListCtrl* ctrl)
{
if (ctrl)
{
ctrl->clearRows();
if (!sBitmapList.empty())
{
for (local_list_iter iter = sBitmapList.begin();
iter != sBitmapList.end(); iter++)
{
LLSD element;
element["columns"][0]["column"] = "unit_name";
element["columns"][0]["type"] = "text";
element["columns"][0]["value"] = (*iter)->getShortName();
element["columns"][1]["column"] = "unit_id_HIDDEN";
element["columns"][1]["type"] = "text";
element["columns"][1]["value"] = (*iter)->getTrackingID();
ctrl->addElement(element);
}
}
}
}
void LLLocalBitmapMgr::doUpdates()
{
// preventing theoretical overlap in cases with huge number of loaded images.
sTimer.stopTimer();
sNeedsRebake = false;
for (local_list_iter iter = sBitmapList.begin(); iter != sBitmapList.end(); iter++)
{
(*iter)->updateSelf();
}
doRebake();
sTimer.startTimer();
}
void LLLocalBitmapMgr::setNeedsRebake()
{
sNeedsRebake = true;
}
void LLLocalBitmapMgr::doRebake()
{ /* separated that from doUpdates to insure a rebake can be called separately during deletion */
if (sNeedsRebake)
{
gAgentAvatarp->forceBakeAllTextures(LL_LOCAL_SLAM_FOR_DEBUG);
sNeedsRebake = false;
}
}

View File

@ -0,0 +1,129 @@
/**
* @file lllocalbitmaps.h
* @author Vaalith Jinn
* @brief Local Bitmap Browser header
*
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2011, 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$
*/
#ifndef LL_LOCALBITMAPS_H
#define LL_LOCALBITMAPS_H
#include "lleventtimer.h"
#include "llwearabletype.h"
#include "llvoavatardefines.h"
class LLScrollListCtrl;
class LLLocalBitmap
{
public: /* main */
LLLocalBitmap(std::string filename);
~LLLocalBitmap();
bool updateSelf(bool first_update = false);
public: /* accessors */
std::string getFilename();
std::string getShortName();
LLUUID getTrackingID();
LLUUID getWorldID();
bool getValid();
private: /* maintenance */
bool decodeBitmap(LLPointer<LLImageRaw> raw);
void replaceIDs(LLUUID old_id, LLUUID new_id); // VAA TODO add default id replacers for sculpt and avatar layer.
private: /* id replacement */
void updateUserPrims(LLUUID old_id, LLUUID new_id);
void updateUserSculpts(LLUUID old_id, LLUUID new_id);
void updateUserLayers(LLUUID old_id, LLUUID new_id, LLWearableType::EType type);
LLVOAvatarDefines::ETextureIndex getTexIndex(LLWearableType::EType type, LLVOAvatarDefines::EBakedTextureIndex baked_texind);
private: /* enums */
enum ELinkStatus
{
LS_ON,
LS_BROKEN,
};
enum EExtension
{
ET_IMG_BMP,
ET_IMG_TGA,
ET_IMG_JPG,
ET_IMG_PNG
};
private: /* members */
std::string mFilename;
std::string mShortName;
LLUUID mTrackingID;
LLUUID mWorldID;
bool mValid;
LLSD mLastModified;
EExtension mExtension;
ELinkStatus mLinkStatus;
S32 mUpdateRetries;
};
class LLLocalBitmapTimer : public LLEventTimer
{
public:
LLLocalBitmapTimer();
~LLLocalBitmapTimer();
public:
void startTimer();
void stopTimer();
bool isRunning();
BOOL tick();
};
class LLLocalBitmapMgr
{
public:
LLLocalBitmapMgr();
~LLLocalBitmapMgr();
public:
static bool addUnit();
static void delUnit(LLUUID tracking_id);
static LLUUID getWorldID(LLUUID tracking_id);
static std::string getFilename(LLUUID tracking_id);
static void feedScrollList(LLScrollListCtrl* ctrl);
static void doUpdates();
static void setNeedsRebake();
static void doRebake();
private:
static std::list<LLLocalBitmap*> sBitmapList;
static LLLocalBitmapTimer sTimer;
static bool sNeedsRebake;
typedef std::list<LLLocalBitmap*>::iterator local_list_iter;
};
#endif

View File

@ -67,6 +67,9 @@
#include "lluictrlfactory.h"
#include "lltrans.h"
#include "llradiogroup.h"
#include "llfloaterreg.h"
#include "lllocalbitmaps.h"
static const S32 HPAD = 4;
static const S32 VPAD = 4;
@ -78,6 +81,8 @@ static const F32 CONTEXT_CONE_IN_ALPHA = 0.0f;
static const F32 CONTEXT_CONE_OUT_ALPHA = 1.f;
static const F32 CONTEXT_FADE_TIME = 0.08f;
static const S32 LOCAL_TRACKING_ID_COLUMN = 1;
//static const char CURRENT_IMAGE_NAME[] = "Current Texture";
//static const char WHITE_IMAGE_NAME[] = "Blank Texture";
//static const char NO_IMAGE_NAME[] = "None";
@ -142,6 +147,12 @@ public:
static void onApplyImmediateCheck(LLUICtrl* ctrl, void* userdata);
void onTextureSelect( const LLTextureEntry& te );
static void onModeSelect(LLUICtrl* ctrl, void *userdata);
static void onBtnAdd(void* userdata);
static void onBtnRemove(void* userdata);
static void onBtnUpload(void* userdata);
static void onLocalScrollCommit(LLUICtrl* ctrl, void* userdata);
protected:
LLPointer<LLViewerTexture> mTexturep;
LLTextureCtrl* mOwner;
@ -169,8 +180,10 @@ protected:
BOOL mNoCopyTextureSelected;
F32 mContextConeOpacity;
LLSaveFolderState mSavedFolderState;
BOOL mSelectedItemPinned;
LLRadioGroup* mModeSelector;
LLScrollListCtrl* mLocalScrollCtrl;
};
LLFloaterTexturePicker::LLFloaterTexturePicker(
@ -437,6 +450,17 @@ BOOL LLFloaterTexturePicker::postBuild()
mInventoryPanel->setSelection(findItemID(mImageAssetID, FALSE), TAKE_FOCUS_NO);
}
mModeSelector = getChild<LLRadioGroup>("mode_selection");
mModeSelector->setCommitCallback(onModeSelect, this);
mModeSelector->setSelectedIndex(0, 0);
childSetAction("l_add_btn", LLFloaterTexturePicker::onBtnAdd, this);
childSetAction("l_rem_btn", LLFloaterTexturePicker::onBtnRemove, this);
childSetAction("l_upl_btn", LLFloaterTexturePicker::onBtnUpload, this);
mLocalScrollCtrl = getChild<LLScrollListCtrl>("l_name_list");
mLocalScrollCtrl->setCommitCallback(onLocalScrollCommit, this);
LLLocalBitmapMgr::feedScrollList(mLocalScrollCtrl);
mNoCopyTextureSelected = FALSE;
@ -748,7 +772,15 @@ void LLFloaterTexturePicker::onBtnSelect(void* userdata)
LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
if (self->mOwner)
{
self->mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_SELECT);
LLUUID local_id = LLUUID::null;
if (self->mLocalScrollCtrl->getVisible() && !self->mLocalScrollCtrl->getAllSelected().empty())
{
LLUUID temp_id = self->mLocalScrollCtrl->getFirstSelected()->getColumn(LOCAL_TRACKING_ID_COLUMN)->getValue().asUUID();
local_id = LLLocalBitmapMgr::getWorldID(temp_id);
}
self->mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_SELECT, local_id);
}
self->closeFloater();
}
@ -791,6 +823,112 @@ void LLFloaterTexturePicker::onSelectionChange(const std::deque<LLFolderViewItem
}
}
// static
void LLFloaterTexturePicker::onModeSelect(LLUICtrl* ctrl, void *userdata)
{
LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
bool mode = (self->mModeSelector->getSelectedIndex() == 0);
self->getChild<LLButton>("Default")->setVisible(mode);
self->getChild<LLButton>("Blank")->setVisible(mode);
self->getChild<LLButton>("None")->setVisible(mode);
self->getChild<LLButton>("Pipette")->setVisible(mode);
self->getChild<LLFilterEditor>("inventory search editor")->setVisible(mode);
self->getChild<LLInventoryPanel>("inventory panel")->setVisible(mode);
/*self->getChild<LLCheckBox>("show_folders_check")->setVisible(mode);
no idea under which conditions the above is even shown, needs testing. */
self->getChild<LLButton>("l_add_btn")->setVisible(!mode);
self->getChild<LLButton>("l_rem_btn")->setVisible(!mode);
self->getChild<LLButton>("l_upl_btn")->setVisible(!mode);
self->getChild<LLScrollListCtrl>("l_name_list")->setVisible(!mode);
}
// static
void LLFloaterTexturePicker::onBtnAdd(void* userdata)
{
if (LLLocalBitmapMgr::addUnit() == true)
{
LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
LLLocalBitmapMgr::feedScrollList(self->mLocalScrollCtrl);
}
}
// static
void LLFloaterTexturePicker::onBtnRemove(void* userdata)
{
LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
std::vector<LLScrollListItem*> selected_items = self->mLocalScrollCtrl->getAllSelected();
if (!selected_items.empty())
{
for(std::vector<LLScrollListItem*>::iterator iter = selected_items.begin();
iter != selected_items.end(); iter++)
{
LLScrollListItem* list_item = *iter;
if (list_item)
{
LLUUID tracking_id = list_item->getColumn(LOCAL_TRACKING_ID_COLUMN)->getValue().asUUID();
LLLocalBitmapMgr::delUnit(tracking_id);
}
}
self->getChild<LLButton>("l_rem_btn")->setEnabled(false);
self->getChild<LLButton>("l_upl_btn")->setEnabled(false);
LLLocalBitmapMgr::feedScrollList(self->mLocalScrollCtrl);
}
}
// static
void LLFloaterTexturePicker::onBtnUpload(void* userdata)
{
LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
std::vector<LLScrollListItem*> selected_items = self->mLocalScrollCtrl->getAllSelected();
if (selected_items.empty())
{
return;
}
/* currently only allows uploading one by one, picks the first item from the selection list. (not the vector!)
in the future, it might be a good idea to check the vector size and if more than one units is selected - opt for multi-image upload. */
LLUUID tracking_id = (LLUUID)self->mLocalScrollCtrl->getSelectedItemLabel(LOCAL_TRACKING_ID_COLUMN);
std::string filename = LLLocalBitmapMgr::getFilename(tracking_id);
if (!filename.empty())
{
LLFloaterReg::showInstance("upload_image", LLSD(filename));
}
}
//static
void LLFloaterTexturePicker::onLocalScrollCommit(LLUICtrl* ctrl, void* userdata)
{
LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
std::vector<LLScrollListItem*> selected_items = self->mLocalScrollCtrl->getAllSelected();
bool has_selection = !selected_items.empty();
self->getChild<LLButton>("l_rem_btn")->setEnabled(has_selection);
self->getChild<LLButton>("l_upl_btn")->setEnabled(has_selection && (selected_items.size() < 2));
/* since multiple-localbitmap upload is not implemented, upl button gets disabled if more than one is selected. */
if (has_selection)
{
LLUUID tracking_id = (LLUUID)self->mLocalScrollCtrl->getSelectedItemLabel(LOCAL_TRACKING_ID_COLUMN);
LLUUID inworld_id = LLLocalBitmapMgr::getWorldID(tracking_id);
self->mOwner->setImageAssetID(inworld_id);
if (self->childGetValue("apply_immediate_check").asBoolean())
{
self->mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_CHANGE, inworld_id);
}
}
}
// static
void LLFloaterTexturePicker::onShowFolders(LLUICtrl* ctrl, void *user_data)
{
@ -1126,7 +1264,7 @@ void LLTextureCtrl::onFloaterClose()
mFloaterHandle.markDead();
}
void LLTextureCtrl::onFloaterCommit(ETexturePickOp op)
void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLUUID id)
{
LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterHandle.get();
@ -1139,14 +1277,24 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op)
// (i.e. op == TEXTURE_SELECT) or texture changes via DnD.
else if (mCommitOnSelection || op == TEXTURE_SELECT)
mViewModel->setDirty(); // *TODO: shouldn't we be using setValue() here?
if( floaterp->isDirty() )
if(floaterp->isDirty() || id.notNull()) // mModelView->setDirty does not work.
{
setTentative( FALSE );
mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE);
lldebugs << "mImageItemID: " << mImageItemID << llendl;
mImageAssetID = floaterp->getAssetID();
lldebugs << "mImageAssetID: " << mImageAssetID << llendl;
if (id.notNull())
{
mImageItemID = id;
mImageAssetID = id;
}
else
{
mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE);
lldebugs << "mImageItemID: " << mImageItemID << llendl;
mImageAssetID = floaterp->getAssetID();
lldebugs << "mImageAssetID: " << mImageAssetID << llendl;
}
if (op == TEXTURE_SELECT && mOnSelectCallback)
{
mOnSelectCallback( this, LLSD() );

View File

@ -161,7 +161,7 @@ public:
void closeDependentFloater();
void onFloaterClose();
void onFloaterCommit(ETexturePickOp op);
void onFloaterCommit(ETexturePickOp op, LLUUID id = LLUUID::null);
// This call is returned when a drag is detected. Your callback
// should return TRUE if the drag is acceptable.

View File

@ -65,6 +65,7 @@ class LLViewerTextureList
friend class LLTextureView;
friend class LLViewerTextureManager;
friend class LLLocalBitmap;
public:
static BOOL createUploadFile(const std::string& filename, const std::string& out_filename, const U8 codec);

View File

@ -810,6 +810,20 @@ const LLLocalTextureObject* LLWearable::getLocalTextureObject(S32 index) const
return NULL;
}
std::vector<LLLocalTextureObject*> LLWearable::getLocalTextureListSeq()
{
std::vector<LLLocalTextureObject*> result;
for(te_map_t::const_iterator iter = mTEMap.begin();
iter != mTEMap.end(); iter++)
{
LLLocalTextureObject* lto = iter->second;
result.push_back(lto);
}
return result;
}
void LLWearable::setLocalTextureObject(S32 index, LLLocalTextureObject &lto)
{
if( mTEMap.find(index) != mTEMap.end() )

View File

@ -106,6 +106,7 @@ public:
LLLocalTextureObject* getLocalTextureObject(S32 index);
const LLLocalTextureObject* getLocalTextureObject(S32 index) const;
std::vector<LLLocalTextureObject*> getLocalTextureListSeq();
void setLocalTextureObject(S32 index, LLLocalTextureObject &lto);
void addVisualParam(LLVisualParam *param);

View File

@ -11,7 +11,38 @@
help_topic="texture_picker"
title="PICK: TEXTURE"
width="410">
<floater.string
<!-- top static -->
<radio_group
control_name="mode_selection"
height="20"
layout="topleft"
left="165"
top="4"
name="mode_selection"
follows="right|top">
<radio_item
label="Inventory"
name="inventory"
tool_tip="Select texture from your inventory."
top_delta="20"
layout="topleft"
height="16"
left="0"
value="0"
width="80" />
<radio_item
label="Computer"
name="local"
tool_tip="Select texture from your own computer. Note: Only you will see the texture."
left_pad="15"
layout="topleft"
top_delta="0"
height="16"
value="1"
width="75" />
</radio_group>
<floater.string
name="choose_picture">
Click to choose a picture
</floater.string>
@ -43,61 +74,52 @@
left_delta="0"
name="unknown"
top_pad="80"
width="163">
width="">
Size: [DIMENSIONS]
</text>
<!-- middle: inventory mode -->
<button
enabled="false"
follows="left|bottom"
height="20"
follows="left|top"
height="18"
label="Default"
label_selected="Default"
layout="topleft"
left_delta="0"
left_delta="90"
name="Default"
top_pad="4"
width="80" />
width="73" />
<button
enabled="false"
follows="left|bottom"
height="20"
label="None"
label_selected="None"
layout="topleft"
left_pad="4"
name="None"
top_delta="0"
width="80" />
<button
follows="left|bottom"
follows="left|top"
height="20"
label="Blank"
label_selected="Blank"
layout="topleft"
left="4"
left_delta="0"
name="Blank"
top_pad="5"
width="80" />
<button
follows="left|bottom"
width="73" />
<button
enabled="false"
follows="left|top"
height="20"
label="None"
label_selected="None"
layout="topleft"
left_delta="0"
name="None"
top_pad="5"
width="73" />
<button
follows="left|top"
height="28"
image_selected="eye_button_active.tga"
image_unselected="eye_button_inactive.tga"
layout="topleft"
left_pad="50"
top_delta="3"
left_delta="-80"
top_delta="-25"
name="Pipette"
width="28" />
<check_box
follows="left|bottom"
height="20"
initial_value="true"
label="Apply now"
layout="topleft"
left="4"
name="apply_immediate_check"
top="262"
width="120" />
<filter_editor
follows="left|top|right"
height="23"
@ -128,23 +150,89 @@
top_pad="0"
left_delta="-3"
width="200" />
<button
follows="right|bottom"
<!-- middle: local mode -->
<button
follows="left|top"
height="18"
label="Add"
label_selected="Add"
layout="topleft"
left="94"
top="-101"
name="l_add_btn"
width="73"
visible="false"/>
<button
enabled="false"
follows="left|top"
height="20"
label="Remove"
label_selected="Remove"
layout="topleft"
left_delta="0"
name="l_rem_btn"
top_pad="5"
width="73"
visible="false"/>
<button
enabled="false"
follows="left|top"
height="20"
label="Upload"
label_selected="Upload"
layout="topleft"
left_delta="0"
name="l_upl_btn"
top_pad="5"
width="73"
visible="false"/>
<scroll_list
name="l_name_list"
left="170"
top="22"
width="235"
height="233"
follows="left|top|right|bottom"
column_padding="0"
can_resize="false"
draw_heading="true"
multi_select="true"
search_column="1"
visible="false">
<column name="unit_name" label="Name" dynamicwidth="true" />
<column name="unit_id_HIDDEN" label="ID" width="0" />
</scroll_list>
<!-- bottom static -->
<button
follows="bottom"
height="20"
label="OK"
label_selected="OK"
layout="topleft"
right="-120"
left="95"
top="-30"
name="Select"
width="100" />
<button
follows="right|bottom"
follows="bottom"
height="20"
label="Cancel"
label_selected="Cancel"
layout="topleft"
right="-10"
left_pad="5"
left_delta="120"
top_delta="0"
name="Cancel"
width="100" />
<check_box
follows="left|bottom"
height="20"
initial_value="true"
label="Apply now"
layout="topleft"
left="6"
name="apply_immediate_check"
top_delta="0"
width="120" />
</floater>

View File

@ -1,150 +0,0 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<floater
legacy_header_height="18"
can_minimize="false"
can_resize="true"
height="290"
layout="topleft"
min_height="290"
min_width="410"
name="texture picker"
help_topic="texture_picker"
title="PICK: TEXTURE"
width="410">
<floater.string
name="choose_picture">
Click to choose a picture
</floater.string>
<floater.string
name="pick title">
Pick:
</floater.string>
<text
type="string"
length="1"
follows="left|top"
text_color="White"
font="SansSerifBig"
halign="center"
height="17"
layout="topleft"
left="4"
name="Multiple"
top="96"
width="163">
Multiple textures
</text>
<text
type="string"
length="1"
follows="left|top"
height="14"
layout="topleft"
left_delta="0"
name="unknown"
top_pad="80"
width="163">
Size: [DIMENSIONS]
</text>
<button
enabled="false"
follows="left|bottom"
height="20"
label="Default"
label_selected="Default"
layout="topleft"
left_delta="0"
name="Default"
top_pad="4"
width="80" />
<button
enabled="false"
follows="left|bottom"
height="20"
label="None"
label_selected="None"
layout="topleft"
left_pad="4"
name="None"
top_delta="0"
width="80" />
<button
follows="left|bottom"
height="20"
label="Blank"
label_selected="Blank"
layout="topleft"
left="4"
name="Blank"
top_pad="5"
width="80" />
<button
follows="left|bottom"
height="28"
image_selected="eye_button_active.tga"
image_unselected="eye_button_inactive.tga"
layout="topleft"
left_pad="50"
top_delta="3"
name="Pipette"
width="28" />
<check_box
follows="left|bottom"
height="20"
initial_value="true"
label="Apply now"
layout="topleft"
left="4"
name="apply_immediate_check"
top="262"
width="120" />
<filter_editor
follows="left|top|right"
height="23"
label="Filter Textures"
layout="topleft"
left="175"
name="inventory search editor"
top="20"
width="231" />
<inventory_panel
allow_multi_select="false"
bg_visible="true"
bg_alpha_color="DkGray2"
border="false"
follows="all"
height="200"
layout="topleft"
left_delta="0"
name="inventory panel"
top_pad="4"
width="231" />
<check_box
height="14"
initial_value="false"
label="Show folders"
layout="topleft"
name="show_folders_check"
top_pad="0"
left_delta="-3"
width="200" />
<button
follows="right|bottom"
height="20"
label="OK"
label_selected="OK"
layout="topleft"
right="-120"
name="Select"
width="100" />
<button
follows="right|bottom"
height="20"
label="Cancel"
label_selected="Cancel"
layout="topleft"
right="-10"
left_pad="5"
name="Cancel"
width="100" />
</floater>