# Conflicts:
#	indra/llimagej2coj/llimagej2coj.cpp
#	indra/newview/llfloaterworldmap.cpp
#	indra/newview/llfloaterworldmap.h
#	indra/newview/llviewertexturelist.cpp
#	indra/newview/skins/default/xui/en/floater_world_map.xml
master
Ansariel 2025-05-23 13:13:24 +02:00
commit 355a80ab91
18 changed files with 719 additions and 325 deletions

View File

@ -1,14 +1,23 @@
name: Run QA Test # Runs automated tests on a self-hosted QA machine
name: Run QA Test # Runs automated tests on self-hosted QA machines
permissions:
contents: read
on:
workflow_run:
workflows: ["Build"]
types:
- completed
workflow_dispatch:
inputs:
build_id:
description: 'Build workflow run ID (e.g. For github.com/secondlife/viewer/actions/runs/1234567890 the ID is 1234567890)'
required: true
default: '14806728332'
concurrency:
group: qa-test-run
cancel-in-progress: true # Cancels any queued job when a new one starts
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false # Prevents cancellation of in-progress jobs
jobs:
debug-workflow:
@ -26,39 +35,165 @@ jobs:
echo "GitHub Workflow Name: ${{ github.workflow }}"
install-viewer-and-run-tests:
runs-on: [self-hosted, qa-machine]
# Run test only on successful builds of Second_Life_X branches
strategy:
matrix:
include:
- os: windows
runner: qa-windows-atlas
artifact: Windows-installer
install-path: 'C:\viewer-automation-main'
- os: windows
runner: qa-dan-asus
artifact: Windows-installer
install-path: 'C:\viewer-automation-main'
# Commented out until mac runner is available
# - os: mac
# runner: qa-mac
# artifact: Mac-installer
# install-path: '$HOME/Documents/viewer-automation'
fail-fast: false
runs-on: [self-hosted, "${{ matrix.runner }}"]
# Run test only on successful builds of Second_Life_X branches or on manual dispatch
if: >
(github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'success' &&
(
startsWith(github.event.workflow_run.head_branch, 'Second_Life')
)
startsWith(github.event.workflow_run.head_branch, 'Second_Life')) ||
github.event_name == 'workflow_dispatch'
steps:
- name: Temporarily Allow PowerShell Scripts (Process Scope)
# Windows-specific steps
- name: Set Build ID
if: matrix.os == 'windows'
shell: pwsh
run: |
if ("${{ github.event_name }}" -eq "workflow_dispatch") {
echo "BUILD_ID=${{ github.event.inputs.build_id }}" | Out-File -FilePath $env:GITHUB_ENV -Append
echo "ARTIFACTS_URL=https://api.github.com/repos/secondlife/viewer/actions/runs/${{ github.event.inputs.build_id }}/artifacts" | Out-File -FilePath $env:GITHUB_ENV -Append
} else {
echo "BUILD_ID=${{ github.event.workflow_run.id }}" | Out-File -FilePath $env:GITHUB_ENV -Append
echo "ARTIFACTS_URL=https://api.github.com/repos/secondlife/viewer/actions/runs/${{ github.event.workflow_run.id }}/artifacts" | Out-File -FilePath $env:GITHUB_ENV -Append
}
- name: Temporarily Allow PowerShell Scripts (Windows)
if: matrix.os == 'windows'
shell: pwsh
run: |
Set-ExecutionPolicy RemoteSigned -Scope Process -Force
- name: Verify viewer-sikulix-main Exists
run: |
if (-Not (Test-Path -Path 'C:\viewer-sikulix-main')) {
Write-Host '❌ Error: viewer-sikulix not found on runner!'
exit 1
}
Write-Host '✅ viewer-sikulix is already available.'
- name: Fetch & Download Windows Installer Artifact
- name: Verify viewer-automation-main Exists (Windows)
if: matrix.os == 'windows'
shell: pwsh
run: |
$BUILD_ID = "${{ github.event.workflow_run.id }}"
$ARTIFACTS_URL = "https://api.github.com/repos/secondlife/viewer/actions/runs/$BUILD_ID/artifacts"
if (-Not (Test-Path -Path '${{ matrix.install-path }}')) {
Write-Host '❌ Error: viewer-automation folder not found on runner!'
exit 1
}
Write-Host '✅ viewer-automation folder is provided.'
- name: Verify viewer-automation-main is Up-To-Date (Windows)
if: matrix.os == 'windows'
shell: pwsh
continue-on-error: true
run: |
cd ${{ matrix.install-path }}
Write-Host "Checking for repository updates..."
# Check if .git directory exists
if (Test-Path -Path ".git") {
try {
# Save local changes instead of discarding them
git stash push -m "Automated stash before update $(Get-Date)"
Write-Host "Local changes saved (if any)"
# Update the repository
git pull
Write-Host "✅ Repository updated successfully"
# Try to restore local changes if any were stashed
$stashList = git stash list
if ($stashList -match "Automated stash before update") {
try {
git stash pop
Write-Host "✅ Local changes restored successfully"
} catch {
Write-Host "⚠️ Conflict when restoring local changes"
# Save the conflicted state in a new branch for later review
$branchName = "conflict-recovery-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
git checkout -b $branchName
Write-Host "✅ Created branch '$branchName' with conflicted state"
# For test execution, revert to a clean state
git reset --hard HEAD
Write-Host "✅ Reset to clean state for test execution"
}
}
} catch {
Write-Host "⚠️ Could not update repository: $_"
Write-Host "Continuing with existing files..."
}
} else {
Write-Host "⚠️ Not a Git repository, using existing files"
}
- name: Verify Python Installation (Windows)
if: matrix.os == 'windows'
shell: pwsh
run: |
try {
$pythonVersion = (python --version)
Write-Host "✅ Python found: $pythonVersion"
} catch {
Write-Host "❌ Error: Python not found in PATH. Please install Python on this runner."
exit 1
}
- name: Setup Python Virtual Environment (Windows)
if: matrix.os == 'windows'
shell: pwsh
run: |
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
cd ${{ matrix.install-path }}
if (-Not (Test-Path -Path ".venv")) {
Write-Host "Creating virtual environment..."
python -m venv .venv
} else {
Write-Host "Using existing virtual environment"
}
# Direct environment activation to avoid script execution issues
$env:VIRTUAL_ENV = "$PWD\.venv"
$env:PATH = "$env:VIRTUAL_ENV\Scripts;$env:PATH"
# Install dependencies
if (Test-Path -Path "requirements.txt") {
Write-Host "Installing dependencies from requirements.txt..."
pip install -r requirements.txt
# Install Playwright browsers - add this line
Write-Host "Installing Playwright browsers..."
python -m playwright install
} else {
pip install outleap requests behave playwright
# Install Playwright browsers - add this line
Write-Host "Installing Playwright browsers..."
python -m playwright install
}
- name: Fetch & Download Installer Artifact (Windows)
if: matrix.os == 'windows'
shell: pwsh
run: |
$BUILD_ID = "${{ env.BUILD_ID }}"
$ARTIFACTS_URL = "${{ env.ARTIFACTS_URL }}"
# Fetch the correct artifact URL
$response = Invoke-RestMethod -Headers @{Authorization="token ${{ secrets.GITHUB_TOKEN }}" } -Uri $ARTIFACTS_URL
$ARTIFACT_NAME = ($response.artifacts | Where-Object { $_.name -eq "Windows-installer" }).archive_download_url
$ARTIFACT_NAME = ($response.artifacts | Where-Object { $_.name -eq "${{ matrix.artifact }}" }).archive_download_url
if (-Not $ARTIFACT_NAME) {
Write-Host "❌ Error: Windows-installer artifact not found!"
Write-Host "❌ Error: ${{ matrix.artifact }} artifact not found!"
exit 1
}
@ -74,16 +209,19 @@ jobs:
# Ensure download succeeded
if (-Not (Test-Path $InstallerPath)) {
Write-Host "❌ Error: Failed to download Windows-installer.zip"
Write-Host "❌ Error: Failed to download ${{ matrix.artifact }}.zip"
exit 1
}
- name: Extract Installer & Locate Executable
# Set the path for other steps
echo "DOWNLOAD_PATH=$DownloadPath" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Extract Installer & Locate Executable (Windows)
if: matrix.os == 'windows'
shell: pwsh
run: |
# Explicitly set BUILD_ID again (since it does not appear to persist across steps)
$BUILD_ID = "${{ github.event.workflow_run.id }}"
$ExtractPath = "$env:TEMP\secondlife-build-$BUILD_ID"
$BUILD_ID = "${{ env.BUILD_ID }}"
$ExtractPath = "${{ env.DOWNLOAD_PATH }}"
$InstallerZip = "$ExtractPath\installer.zip"
# Print paths for debugging
@ -113,16 +251,19 @@ jobs:
Write-Host "✅ Installer found: $INSTALLER_PATH"
echo "INSTALLER_PATH=$INSTALLER_PATH" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Install Second Life Using Task Scheduler (Bypass UAC)
- name: Install Second Life (Windows)
if: matrix.os == 'windows'
shell: pwsh
run: |
# Windows - Use Task Scheduler to bypass UAC
$action = New-ScheduledTaskAction -Execute "${{ env.INSTALLER_PATH }}" -Argument "/S"
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
$task = New-ScheduledTask -Action $action -Principal $principal
Register-ScheduledTask -TaskName "SilentSLInstaller" -InputObject $task -Force
Start-ScheduledTask -TaskName "SilentSLInstaller"
- name: Wait for Installation to Complete
- name: Wait for Installation to Complete (Windows)
if: matrix.os == 'windows'
shell: pwsh
run: |
Write-Host "Waiting for the Second Life installer to finish..."
@ -133,18 +274,16 @@ jobs:
Write-Host "✅ Installation completed!"
- name: Cleanup Task Scheduler Entry
- name: Cleanup After Installation (Windows)
if: matrix.os == 'windows'
shell: pwsh
run: |
# Cleanup Task Scheduler Entry
Unregister-ScheduledTask -TaskName "SilentSLInstaller" -Confirm:$false
Write-Host "✅ Task Scheduler entry removed."
- name: Delete Installer ZIP
shell: pwsh
run: |
# Explicitly set BUILD_ID again
$BUILD_ID = "${{ github.event.workflow_run.id }}"
$DeletePath = "$env:TEMP\secondlife-build-$BUILD_ID\installer.zip"
# Delete Installer ZIP
$DeletePath = "${{ env.DOWNLOAD_PATH }}\installer.zip"
Write-Host "Checking if installer ZIP exists: $DeletePath"
@ -156,13 +295,281 @@ jobs:
Write-Host "⚠️ Warning: ZIP file does not exist, skipping deletion."
}
- name: Run QA Test Script
- name: Run QA Test Script (Windows)
if: matrix.os == 'windows'
shell: pwsh
run: |
Write-Host "Running QA Test script..."
python C:\viewer-sikulix-main\runTests.py
Write-Host "Running QA Test script on Windows runner: ${{ matrix.runner }}..."
cd ${{ matrix.install-path }}
# Activate virtual environment
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
$env:VIRTUAL_ENV = "$PWD\.venv"
$env:PATH = "$env:VIRTUAL_ENV\Scripts;$env:PATH"
# Set runner name as environment variable
$env:RUNNER_NAME = "${{ matrix.runner }}"
# Run the test script
python runTests.py
# Mac-specific steps
- name: Set Build ID (Mac)
if: matrix.os == 'mac'
shell: bash
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "BUILD_ID=${{ github.event.inputs.build_id }}" >> $GITHUB_ENV
echo "ARTIFACTS_URL=https://api.github.com/repos/secondlife/viewer/actions/runs/${{ github.event.inputs.build_id }}/artifacts" >> $GITHUB_ENV
else
echo "BUILD_ID=${{ github.event.workflow_run.id }}" >> $GITHUB_ENV
echo "ARTIFACTS_URL=https://api.github.com/repos/secondlife/viewer/actions/runs/${{ github.event.workflow_run.id }}/artifacts" >> $GITHUB_ENV
fi
- name: Verify viewer-automation-main Exists (Mac)
if: matrix.os == 'mac'
shell: bash
run: |
if [ ! -d "${{ matrix.install-path }}" ]; then
echo "❌ Error: viewer-automation folder not found on runner!"
exit 1
fi
echo "✅ viewer-automation is provided."
- name: Verify viewer-automation-main is Up-To-Date (Mac)
if: matrix.os == 'mac'
shell: bash
continue-on-error: true
run: |
cd ${{ matrix.install-path }}
echo "Checking for repository updates..."
# Check if .git directory exists
if [ -d ".git" ]; then
# Save local changes instead of discarding them
git stash push -m "Automated stash before update $(date)"
echo "Local changes saved (if any)"
# Update the repository
git pull || echo "⚠️ Could not update repository"
echo "✅ Repository updated (or attempted update)"
# Try to restore local changes if any were stashed
if git stash list | grep -q "Automated stash before update"; then
# Try to pop the stash, but be prepared for conflicts
if ! git stash pop; then
echo "⚠️ Conflict when restoring local changes"
# Save the conflicted state in a new branch for later review
branch_name="conflict-recovery-$(date +%Y%m%d-%H%M%S)"
git checkout -b "$branch_name"
echo "✅ Created branch '$branch_name' with conflicted state"
# For test execution, revert to a clean state
git reset --hard HEAD
echo "✅ Reset to clean state for test execution"
else
echo "✅ Local changes restored successfully"
fi
fi
else
echo "⚠️ Not a Git repository, using existing files"
fi
- name: Verify Python Installation (Mac)
if: matrix.os == 'mac'
shell: bash
run: |
if command -v python3 &> /dev/null; then
PYTHON_VERSION=$(python3 --version)
echo "✅ Python found: $PYTHON_VERSION"
else
echo "❌ Error: Python3 not found in PATH. Please install Python on this runner."
exit 1
fi
- name: Setup Python Virtual Environment (Mac)
if: matrix.os == 'mac'
shell: bash
run: |
cd ${{ matrix.install-path }}
# Create virtual environment if it doesn't exist
if [ ! -d ".venv" ]; then
echo "Creating virtual environment..."
python3 -m venv .venv
else
echo "Using existing virtual environment"
fi
# Activate virtual environment
source .venv/bin/activate
# Install dependencies
if [ -f "requirements.txt" ]; then
pip install -r requirements.txt
echo "✅ Installed dependencies from requirements.txt"
# Install Playwright browsers - add this line
echo "Installing Playwright browsers..."
python -m playwright install
else
pip install outleap requests behave playwright
echo "⚠️ requirements.txt not found, installed basic dependencies"
# Install Playwright browsers - add this line
echo "Installing Playwright browsers..."
python -m playwright install
fi
- name: Fetch & Download Installer Artifact (Mac)
if: matrix.os == 'mac'
shell: bash
run: |
# Mac-specific Bash commands
response=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s ${{ env.ARTIFACTS_URL }})
ARTIFACT_NAME=$(echo $response | jq -r '.artifacts[] | select(.name=="${{ matrix.artifact }}") | .archive_download_url')
if [ -z "$ARTIFACT_NAME" ]; then
echo "❌ Error: ${{ matrix.artifact }} artifact not found!"
exit 1
fi
echo "✅ Artifact found: $ARTIFACT_NAME"
# Secure download path
DOWNLOAD_PATH="/tmp/secondlife-build-${{ env.BUILD_ID }}"
mkdir -p $DOWNLOAD_PATH
INSTALLER_PATH="$DOWNLOAD_PATH/installer.zip"
# Download the ZIP
curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -L $ARTIFACT_NAME -o $INSTALLER_PATH
# Ensure download succeeded
if [ ! -f "$INSTALLER_PATH" ]; then
echo "❌ Error: Failed to download ${{ matrix.artifact }}.zip"
exit 1
fi
# Set the path for other steps
echo "DOWNLOAD_PATH=$DOWNLOAD_PATH" >> $GITHUB_ENV
- name: Extract Installer & Locate Executable (Mac)
if: matrix.os == 'mac'
shell: bash
run: |
EXTRACT_PATH="${{ env.DOWNLOAD_PATH }}"
INSTALLER_ZIP="$EXTRACT_PATH/installer.zip"
# Debug output
echo "Extract Path: $EXTRACT_PATH"
echo "Installer ZIP Path: $INSTALLER_ZIP"
# Verify ZIP exists
if [ ! -f "$INSTALLER_ZIP" ]; then
echo "❌ Error: ZIP file not found at $INSTALLER_ZIP!"
exit 1
fi
echo "✅ ZIP file exists and is valid. Extracting..."
# Extract the ZIP
unzip -o "$INSTALLER_ZIP" -d "$EXTRACT_PATH"
# Find DMG file
INSTALLER_PATH=$(find "$EXTRACT_PATH" -name "*.dmg" -type f | head -1)
if [ -z "$INSTALLER_PATH" ]; then
echo "❌ Error: No installer DMG found in the extracted files!"
echo "📂 Extracted Files:"
ls -la "$EXTRACT_PATH"
exit 1
fi
echo "✅ Installer found: $INSTALLER_PATH"
echo "INSTALLER_PATH=$INSTALLER_PATH" >> $GITHUB_ENV
- name: Install Second Life (Mac)
if: matrix.os == 'mac'
shell: bash
run: |
# Mac installation
echo "Mounting DMG installer..."
MOUNT_POINT="/tmp/secondlife-dmg"
mkdir -p "$MOUNT_POINT"
# Mount the DMG
hdiutil attach "${{ env.INSTALLER_PATH }}" -mountpoint "$MOUNT_POINT" -nobrowse
echo "✅ DMG mounted at $MOUNT_POINT"
# Find the app in the mounted DMG
APP_PATH=$(find "$MOUNT_POINT" -name "*.app" -type d | head -1)
if [ -z "$APP_PATH" ]; then
echo "❌ Error: No .app bundle found in the mounted DMG!"
exit 1
fi
echo "Installing application to Applications folder..."
# Copy the app to the Applications folder (or specified install path)
cp -R "$APP_PATH" "${{ matrix.install-path }}"
# Verify the app was copied successfully
if [ ! -d "${{ matrix.install-path }}/$(basename "$APP_PATH")" ]; then
echo "❌ Error: Failed to install application to ${{ matrix.install-path }}!"
exit 1
fi
echo "✅ Application installed successfully to ${{ matrix.install-path }}"
# Save mount point for cleanup
echo "MOUNT_POINT=$MOUNT_POINT" >> $GITHUB_ENV
- name: Wait for Installation to Complete (Mac)
if: matrix.os == 'mac'
shell: bash
run: |
echo "Waiting for installation to complete..."
# Sleep to allow installation to finish (adjust as needed)
sleep 30
echo "✅ Installation completed"
- name: Cleanup After Installation (Mac)
if: matrix.os == 'mac'
shell: bash
run: |
# Mac cleanup
# Unmount the DMG
echo "Unmounting DMG..."
hdiutil detach "${{ env.MOUNT_POINT }}" -force
# Clean up temporary files
echo "Cleaning up temporary files..."
rm -rf "${{ env.DOWNLOAD_PATH }}"
rm -rf "${{ env.MOUNT_POINT }}"
echo "✅ Cleanup completed"
- name: Run QA Test Script (Mac)
if: matrix.os == 'mac'
shell: bash
run: |
echo "Running QA Test script on Mac runner: ${{ matrix.runner }}..."
cd ${{ matrix.install-path }}
# Activate virtual environment
source .venv/bin/activate
# Set runner name as environment variable
export RUNNER_NAME="${{ matrix.runner }}"
# Run the test script
python runTests.py
# - name: Upload Test Results
# uses: actions/upload-artifact@v3
# if: always()
# uses: actions/upload-artifact@v4
# with:
# name: test-results
# path: C:\viewer-sikulix-main\regressionTest\test_results.html
# name: test-results-${{ matrix.runner }}
# path: ${{ matrix.install-path }}/regressionTest/test_results.html

View File

@ -12,7 +12,7 @@ repos:
- id: indent-with-spaces
files: \.(cpp|c|h|inl|py|glsl|cmake)$
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v5.0.0
hooks:
- id: check-xml
- id: mixed-line-ending

View File

@ -114,9 +114,10 @@ std::vector<std::string> LLDir::getFilesInDir(const std::string &dirname)
std::vector<std::string> v;
if (exists(p))
boost::system::error_code ec;
if (exists(p, ec) && !ec.failed())
{
if (is_directory(p))
if (is_directory(p, ec) && !ec.failed())
{
boost::filesystem::directory_iterator end_iter;
for (boost::filesystem::directory_iterator dir_itr(p);

View File

@ -281,10 +281,11 @@ S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 r
S32 height = (h > 0) ? h : 2048;
S32 max_dimension = llmax(width, height); // Find largest dimension
S32 block_area = MAX_BLOCK_SIZE * MAX_BLOCK_SIZE; // Calculated initial block area from established max block size (currently 64)
block_area *= llmax((max_dimension / MAX_BLOCK_SIZE / max_components), 1); // Adjust initial block area by ratio of largest dimension to block size per component
S32 totalbytes = (S32) (block_area * max_components * precision); // First block layer computed before loop without compression rate
S32 block_layers = 1; // Start at layer 1 since first block layer is computed outside loop
while (block_layers < 6) // Walk five layers for the five discards in JPEG2000
S32 max_layers = (S32)llmax(llround(log2f((float)max_dimension) - log2f((float)MAX_BLOCK_SIZE)), 4); // Find number of powers of two between extents and block size to a minimum of 4
block_area *= llmax(max_layers, 1); // Adjust initial block area by max number of layers
S32 totalbytes = (S32) (MIN_LAYER_SIZE * max_components * precision); // Start estimation with a minimum reasonable size
S32 block_layers = 0;
while (block_layers <= max_layers) // Walk the layers
{
if (block_layers <= (5 - discard_level)) // Walk backwards from discard 5 to required discard layer.
totalbytes += (S32) (block_area * max_components * precision * rate); // Add each block layer reduced by assumed compression rate

View File

@ -32,8 +32,6 @@
#include "event.h"
#include "cio.h"
#define MAX_ENCODED_DISCARD_LEVELS 5
// Factory function: see declaration in llimagej2c.cpp
LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl()
{
@ -135,73 +133,96 @@ static void opj_error(const char* msg, void* user_data)
static OPJ_SIZE_T opj_read(void * buffer, OPJ_SIZE_T bytes, void* user_data)
{
llassert(user_data);
llassert(user_data && buffer);
JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
OPJ_SIZE_T remainder = (jpeg_codec->size - jpeg_codec->offset);
if (remainder <= 0)
if (jpeg_codec->offset < 0 || static_cast<OPJ_SIZE_T>(jpeg_codec->offset) >= jpeg_codec->size)
{
jpeg_codec->offset = jpeg_codec->size;
// Indicate end of stream (hacky?)
return (OPJ_OFF_T)-1;
return static_cast<OPJ_SIZE_T>(-1); // Indicate EOF
}
OPJ_SIZE_T to_read = llclamp(U32(bytes), U32(0), U32(remainder));
OPJ_SIZE_T remainder = jpeg_codec->size - static_cast<OPJ_SIZE_T>(jpeg_codec->offset);
OPJ_SIZE_T to_read = (bytes < remainder) ? bytes : remainder;
memcpy(buffer, jpeg_codec->buffer + jpeg_codec->offset, to_read);
jpeg_codec->offset += to_read;
return to_read;
}
static OPJ_SIZE_T opj_write(void * buffer, OPJ_SIZE_T bytes, void* user_data)
{
llassert(user_data);
llassert(user_data && buffer);
JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
OPJ_SIZE_T remainder = jpeg_codec->size - jpeg_codec->offset;
if (remainder < bytes)
OPJ_OFF_T required_offset = jpeg_codec->offset + static_cast<OPJ_OFF_T>(bytes);
// Overflow check
if (required_offset < jpeg_codec->offset)
return 0; // Overflow detected
// Resize if needed (exponential growth)
if (required_offset > static_cast<OPJ_OFF_T>(jpeg_codec->size))
{
OPJ_SIZE_T new_size = jpeg_codec->size + (bytes - remainder);
OPJ_SIZE_T new_size = jpeg_codec->size ? jpeg_codec->size : 1024;
while (required_offset > static_cast<OPJ_OFF_T>(new_size))
new_size *= 2;
const OPJ_SIZE_T MAX_BUFFER_SIZE = 512 * 1024 * 1024; // 512 MB, increase if needed
if (new_size > MAX_BUFFER_SIZE) return 0;
U8* new_buffer = (U8*)ll_aligned_malloc_16(new_size);
memcpy(new_buffer, jpeg_codec->buffer, jpeg_codec->offset);
U8* old_buffer = jpeg_codec->buffer;
if (!new_buffer) return 0; // Allocation failed
if (jpeg_codec->offset > 0)
memcpy(new_buffer, jpeg_codec->buffer, static_cast<size_t>(jpeg_codec->offset));
ll_aligned_free_16(jpeg_codec->buffer);
jpeg_codec->buffer = new_buffer;
ll_aligned_free_16(old_buffer);
jpeg_codec->size = new_size;
}
memcpy(jpeg_codec->buffer + jpeg_codec->offset, buffer, bytes);
jpeg_codec->offset += bytes;
memcpy(jpeg_codec->buffer + jpeg_codec->offset, buffer, static_cast<size_t>(bytes));
jpeg_codec->offset = required_offset;
return bytes;
}
static OPJ_OFF_T opj_skip(OPJ_OFF_T bytes, void* user_data)
{
llassert(user_data);
JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
jpeg_codec->offset += bytes;
if (jpeg_codec->offset > (OPJ_OFF_T)jpeg_codec->size)
OPJ_OFF_T new_offset = jpeg_codec->offset + bytes;
if (new_offset < 0 || new_offset > static_cast<OPJ_OFF_T>(jpeg_codec->size))
{
jpeg_codec->offset = jpeg_codec->size;
// Indicate end of stream
return (OPJ_OFF_T)-1;
}
if (jpeg_codec->offset < 0)
{
// Shouldn't be possible?
jpeg_codec->offset = 0;
// Clamp and indicate EOF or error
jpeg_codec->offset = llclamp<OPJ_OFF_T>(new_offset, 0, static_cast<OPJ_OFF_T>(jpeg_codec->size));
return (OPJ_OFF_T)-1;
}
jpeg_codec->offset = new_offset;
return bytes;
}
static OPJ_BOOL opj_seek(OPJ_OFF_T bytes, void * user_data)
static OPJ_BOOL opj_seek(OPJ_OFF_T offset, void * user_data)
{
llassert(user_data);
JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
jpeg_codec->offset = bytes;
jpeg_codec->offset = llclamp(U32(jpeg_codec->offset), U32(0), U32(jpeg_codec->size));
if (offset < 0 || offset > static_cast<OPJ_OFF_T>(jpeg_codec->size))
return OPJ_FALSE;
jpeg_codec->offset = offset;
return OPJ_TRUE;
}
static void opj_free_user_data(void * user_data)
{
llassert(user_data);
JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
// Don't free, data is managed externally
jpeg_codec->buffer = nullptr;
@ -211,14 +232,54 @@ static void opj_free_user_data(void * user_data)
static void opj_free_user_data_write(void * user_data)
{
llassert(user_data);
JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
// Free, data was allocated here
if (jpeg_codec->buffer)
{
ll_aligned_free_16(jpeg_codec->buffer);
jpeg_codec->buffer = nullptr;
}
jpeg_codec->size = 0;
jpeg_codec->offset = 0;
}
/**
* Estimates the number of layers necessary depending on the image surface (w x h)
*/
static U32 estimate_num_layers(U32 surface)
{
if (surface <= 1024) return 2; // Tiny (≤32×32)
else if (surface <= 16384) return 3; // Small (≤128×128)
else if (surface <= 262144) return 4; // Medium (≤512×512)
else if (surface <= 1048576) return 5; // Up to ~1MP
else return 6; // Up to ~1.52MP
}
/**
* Sets the parameters.tcp_rates according to the number of layers and a last tcp_rate value (which equals to the final compression ratio).
*
* Example for 6 layers:
*
* i = 5, parameters.tcp_rates[6 - 1 - 5] = 8.0f * (1 << (5 << 1)) = 8192 // Layer 5 (lowest quality)
* i = 4, parameters.tcp_rates[6 - 1 - 4] = 8.0f * (1 << (4 << 1)) = 2048 // Layer 4
* i = 3, parameters.tcp_rates[6 - 1 - 3] = 8.0f * (1 << (3 << 1)) = 512 // Layer 3
* i = 2, parameters.tcp_rates[6 - 1 - 2] = 8.0f * (1 << (2 << 1)) = 128 // Layer 2
* i = 1, parameters.tcp_rates[6 - 1 - 1] = 8.0f * (1 << (1 << 1)) = 32 // Layer 1
* i = 0, parameters.tcp_rates[6 - 1 - 0] = 8.0f * (1 << (0 << 1)) = 8 // Layer 0 (highest quality)
*
*/
static void set_tcp_rates(opj_cparameters_t* parameters, U32 num_layers = 1, F32 last_tcp_rate = LAST_TCP_RATE)
{
parameters->tcp_numlayers = num_layers;
for (int i = num_layers - 1; i >= 0; i--)
{
parameters->tcp_rates[num_layers - 1 - i] = last_tcp_rate * static_cast<F32>(1 << (i << 1));
}
}
class JPEG2KDecode : public JPEG2KBase
{
public:
@ -433,15 +494,16 @@ public:
opj_set_default_encoder_parameters(&parameters);
parameters.cod_format = OPJ_CODEC_J2K;
parameters.cp_disto_alloc = 1;
parameters.prog_order = OPJ_RLCP; // should be the default, but, just in case
parameters.cp_disto_alloc = 1; // enable rate allocation by distortion
parameters.max_cs_size = 0; // do not cap max size because we're using tcp_rates and also irrelevant with lossless.
if (reversible)
{
parameters.max_cs_size = 0; // do not limit size for reversible compression
parameters.irreversible = 0; // should be the default, but, just in case
parameters.tcp_numlayers = 1;
/* documentation seems to be wrong, should be 0.0f for lossless, not 1.0f
see https://github.com/uclouvain/openjpeg/blob/39e8c50a2f9bdcf36810ee3d41bcbf1cc78968ae/src/lib/openjp2/j2k.c#L7755
see https://github.com/uclouvain/openjpeg/blob/e7453e398b110891778d8da19209792c69ca7169/src/lib/openjp2/j2k.c#L7817
*/
parameters.tcp_rates[0] = 0.0f;
}
@ -496,53 +558,22 @@ public:
encoder = opj_create_compress(OPJ_CODEC_J2K);
parameters.tcp_mct = (image->numcomps >= 3) ? 1 : 0;
parameters.cod_format = OPJ_CODEC_J2K;
parameters.prog_order = OPJ_RLCP;
parameters.cp_disto_alloc = 1;
parameters.tcp_mct = (image->numcomps >= 3) ? 1 : 0; // no color transform for RGBA images
// if not lossless compression, computes tcp_numlayers and max_cs_size depending on the image dimensions
if( parameters.irreversible ) {
if( parameters.irreversible )
{
// computes a number of layers
U32 surface = rawImageIn.getWidth() * rawImageIn.getHeight();
U32 nb_layers = 1;
U32 s = 64*64;
while (surface > s)
{
nb_layers++;
s *= 4;
}
nb_layers = llclamp(nb_layers, 1, 6);
parameters.tcp_numlayers = nb_layers;
parameters.tcp_rates[nb_layers - 1] = (U32)(1.f / DEFAULT_COMPRESSION_RATE); // 1:8 by default
// gets the necessary number of layers
U32 nb_layers = estimate_num_layers(surface);
// for each subsequent layer, computes its rate and adds surface * numcomps * 1/rate to the max_cs_size
U32 max_cs_size = (U32)(surface * image->numcomps * DEFAULT_COMPRESSION_RATE);
U32 multiplier;
for (int i = nb_layers - 2; i >= 0; i--)
{
if( i == nb_layers - 2 )
{
multiplier = 15;
}
else if( i == nb_layers - 3 )
{
multiplier = 4;
}
else
{
multiplier = 2;
}
parameters.tcp_rates[i] = parameters.tcp_rates[i + 1] * multiplier;
max_cs_size += (U32)(surface * image->numcomps * (1 / parameters.tcp_rates[i]));
}
// fills parameters.tcp_rates and updates parameters.tcp_numlayers
set_tcp_rates(&parameters, nb_layers, LAST_TCP_RATE);
//ensure that we have at least a minimal size
max_cs_size = llmax(max_cs_size, (U32)FIRST_PACKET_SIZE);
parameters.max_cs_size = max_cs_size;
}
if (!opj_setup_encoder(encoder, &parameters, image))
@ -582,7 +613,7 @@ public:
opj_stream_destroy(stream);
}
stream = opj_stream_create(data_size_guess, false);
stream = opj_stream_create(data_size_guess, OPJ_FALSE);
if (!stream)
{
return false;
@ -623,16 +654,15 @@ public:
void setImage(const LLImageRaw& raw)
{
opj_image_cmptparm_t cmptparm[MAX_ENCODED_DISCARD_LEVELS];
memset(&cmptparm[0], 0, MAX_ENCODED_DISCARD_LEVELS * sizeof(opj_image_cmptparm_t));
S32 numcomps = raw.getComponents();
S32 width = raw.getWidth();
S32 height = raw.getHeight();
std::vector<opj_image_cmptparm_t> cmptparm(numcomps);
for (S32 c = 0; c < numcomps; c++)
{
cmptparm[c].prec = 8;
cmptparm[c].prec = 8; // replaces .bpp
cmptparm[c].sgnd = 0;
cmptparm[c].dx = parameters.subsampling_dx;
cmptparm[c].dy = parameters.subsampling_dy;
@ -640,7 +670,7 @@ public:
cmptparm[c].h = height;
}
image = opj_image_create(numcomps, &cmptparm[0], OPJ_CLRSPC_SRGB);
image = opj_image_create(numcomps, cmptparm.data(), OPJ_CLRSPC_SRGB);
image->x1 = width;
image->y1 = height;
@ -652,7 +682,7 @@ public:
{
for (S32 x = 0; x < width; x++)
{
const U8 *pixel = src_datap + (y*width + x) * numcomps;
const U8 *pixel = src_datap + (y * width + x) * numcomps;
for (S32 c = 0; c < numcomps; c++)
{
image->comps[c].data[i] = *pixel;

View File

@ -29,6 +29,8 @@
#include "llimagej2c.h"
const F32 LAST_TCP_RATE = 1.f/DEFAULT_COMPRESSION_RATE; // should be 8, giving a 1:8 ratio
class LLImageJ2COJ : public LLImageJ2CImpl
{
public:

View File

@ -1109,7 +1109,7 @@ void LLScrollListCtrl::deleteItems(const LLSD& sd)
void LLScrollListCtrl::deleteSelectedItems()
{
item_list::iterator iter;
for (iter = mItemList.begin(); iter < mItemList.end(); )
for (iter = mItemList.begin(); iter != mItemList.end(); )
{
LLScrollListItem* itemp = *iter;
if (itemp->getSelected())

View File

@ -112,7 +112,6 @@ LLWindow::LLWindow(LLWindowCallbacks* callbacks, bool fullscreen, U32 flags)
mFullscreen(fullscreen),
mFullscreenWidth(0),
mFullscreenHeight(0),
mFullscreenBits(0),
mFullscreenRefresh(0),
mSupportedResolutions(NULL),
mNumSupportedResolutions(0),

View File

@ -224,7 +224,6 @@ protected:
bool mFullscreen;
S32 mFullscreenWidth;
S32 mFullscreenHeight;
S32 mFullscreenBits;
S32 mFullscreenRefresh;
LLWindowResolution* mSupportedResolutions;
S32 mNumSupportedResolutions;

View File

@ -704,8 +704,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
}
if (dev_mode.dmPelsWidth == width &&
dev_mode.dmPelsHeight == height &&
dev_mode.dmBitsPerPel == BITS_PER_PIXEL)
dev_mode.dmPelsHeight == height)
{
success = true;
if ((dev_mode.dmDisplayFrequency - current_refresh)
@ -745,7 +744,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
// If we found a good resolution, use it.
if (success)
{
success = setDisplayResolution(width, height, BITS_PER_PIXEL, closest_refresh);
success = setDisplayResolution(width, height, closest_refresh);
}
// Keep a copy of the actual current device mode in case we minimize
@ -758,7 +757,6 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
mFullscreen = true;
mFullscreenWidth = dev_mode.dmPelsWidth;
mFullscreenHeight = dev_mode.dmPelsHeight;
mFullscreenBits = dev_mode.dmBitsPerPel;
mFullscreenRefresh = dev_mode.dmDisplayFrequency;
LL_INFOS("Window") << "Running at " << dev_mode.dmPelsWidth
@ -772,7 +770,6 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
mFullscreen = false;
mFullscreenWidth = -1;
mFullscreenHeight = -1;
mFullscreenBits = -1;
mFullscreenRefresh = -1;
std::map<std::string,std::string> args;
@ -1195,7 +1192,7 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo
// If we found a good resolution, use it.
if (success)
{
success = setDisplayResolution(width, height, BITS_PER_PIXEL, closest_refresh);
success = setDisplayResolution(width, height, closest_refresh);
}
// Keep a copy of the actual current device mode in case we minimize
@ -1207,7 +1204,6 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo
mFullscreen = true;
mFullscreenWidth = dev_mode.dmPelsWidth;
mFullscreenHeight = dev_mode.dmPelsHeight;
mFullscreenBits = dev_mode.dmBitsPerPel;
mFullscreenRefresh = dev_mode.dmDisplayFrequency;
LL_INFOS("Window") << "Running at " << dev_mode.dmPelsWidth
@ -1233,7 +1229,6 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo
mFullscreen = false;
mFullscreenWidth = -1;
mFullscreenHeight = -1;
mFullscreenBits = -1;
mFullscreenRefresh = -1;
LL_INFOS("Window") << "Unable to run fullscreen at " << width << "x" << height << LL_ENDL;
@ -3650,7 +3645,7 @@ F32 LLWindowWin32::getPixelAspectRatio()
// Change display resolution. Returns true if successful.
// protected
bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh)
bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 refresh)
{
DEVMODE dev_mode;
::ZeroMemory(&dev_mode, sizeof(DEVMODE));
@ -3662,7 +3657,6 @@ bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 re
{
if (dev_mode.dmPelsWidth == width &&
dev_mode.dmPelsHeight == height &&
dev_mode.dmBitsPerPel == bits &&
dev_mode.dmDisplayFrequency == refresh )
{
// ...display mode identical, do nothing
@ -3674,9 +3668,8 @@ bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 re
dev_mode.dmSize = sizeof(dev_mode);
dev_mode.dmPelsWidth = width;
dev_mode.dmPelsHeight = height;
dev_mode.dmBitsPerPel = bits;
dev_mode.dmDisplayFrequency = refresh;
dev_mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
dev_mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
// CDS_FULLSCREEN indicates that this is a temporary change to the device mode.
LONG cds_result = ChangeDisplaySettings(&dev_mode, CDS_FULLSCREEN);
@ -3686,7 +3679,7 @@ bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 re
if (!success)
{
LL_WARNS("Window") << "setDisplayResolution failed, "
<< width << "x" << height << "x" << bits << " @ " << refresh << LL_ENDL;
<< width << "x" << height << " @ " << refresh << LL_ENDL;
}
return success;
@ -3697,7 +3690,7 @@ bool LLWindowWin32::setFullscreenResolution()
{
if (mFullscreen)
{
return setDisplayResolution( mFullscreenWidth, mFullscreenHeight, mFullscreenBits, mFullscreenRefresh);
return setDisplayResolution( mFullscreenWidth, mFullscreenHeight, mFullscreenRefresh);
}
else
{

View File

@ -153,7 +153,7 @@ protected:
virtual LLSD getNativeKeyData();
// Changes display resolution. Returns true if successful
bool setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh);
bool setDisplayResolution(S32 width, S32 height, S32 refresh);
// Go back to last fullscreen display resolution.
bool setFullscreenResolution();

View File

@ -849,7 +849,7 @@ void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit)
mModelPreview->onLODGLODParamCommit(lod, enforce_tri_limit);
break;
default:
LL_ERRS() << "Only supposed to be called to generate models" << LL_ENDL;
LL_ERRS() << "Only supposed to be called to generate models, val: " << mode << LL_ENDL;
break;
}

View File

@ -345,9 +345,6 @@ LLFloaterWorldMap::LLFloaterWorldMap(const LLSD& key)
mSetToUserPosition(true),
mTrackedLocation(0.0,0.0,0.0),
mTrackedStatus(LLTracker::TRACKING_NOTHING),
mListFriendCombo(nullptr),
mListLandmarkCombo(nullptr),
mListSearchResults(nullptr),
mParcelInfoObserver(nullptr),
mShowParcelInfo(false)
{
@ -410,24 +407,22 @@ bool LLFloaterWorldMap::postBuild()
mTeleportCoordSpinZ = getChild<LLSpinCtrl>("teleport_coordinate_z");
// </FS>
LLComboBox *avatar_combo = getChild<LLComboBox>("friend combo");
avatar_combo->selectFirstItem();
avatar_combo->setPrearrangeCallback( boost::bind(&LLFloaterWorldMap::onAvatarComboPrearrange, this) );
avatar_combo->setTextChangedCallback( boost::bind(&LLFloaterWorldMap::onComboTextEntry, this) );
mListFriendCombo = dynamic_cast<LLCtrlListInterface *>(avatar_combo);
mFriendCombo = getChild<LLComboBox>("friend combo");
mFriendCombo->selectFirstItem();
mFriendCombo->setPrearrangeCallback(boost::bind(&LLFloaterWorldMap::onAvatarComboPrearrange, this));
mFriendCombo->setTextChangedCallback(boost::bind(&LLFloaterWorldMap::onComboTextEntry, this));
mLocationEditor = getChild<LLSearchEditor>("location");
mLocationEditor->setFocusChangedCallback(boost::bind(&LLFloaterWorldMap::onLocationFocusChanged, this, _1));
mLocationEditor->setTextChangedCallback( boost::bind(&LLFloaterWorldMap::onSearchTextEntry, this));
mLocationEditor->setTextChangedCallback(boost::bind(&LLFloaterWorldMap::onSearchTextEntry, this));
getChild<LLScrollListCtrl>("search_results")->setDoubleClickCallback( boost::bind(&LLFloaterWorldMap::onClickTeleportBtn, this));
mListSearchResults = childGetListInterface("search_results");
mSearchResults = getChild<LLScrollListCtrl>("search_results");
mSearchResults->setDoubleClickCallback(boost::bind(&LLFloaterWorldMap::onClickTeleportBtn, this));
LLComboBox *landmark_combo = getChild<LLComboBox>( "landmark combo");
landmark_combo->selectFirstItem();
landmark_combo->setPrearrangeCallback( boost::bind(&LLFloaterWorldMap::onLandmarkComboPrearrange, this) );
landmark_combo->setTextChangedCallback( boost::bind(&LLFloaterWorldMap::onComboTextEntry, this) );
mListLandmarkCombo = dynamic_cast<LLCtrlListInterface *>(landmark_combo);
mLandmarkCombo = getChild<LLComboBox>("landmark combo");
mLandmarkCombo->selectFirstItem();
mLandmarkCombo->setPrearrangeCallback(boost::bind(&LLFloaterWorldMap::onLandmarkComboPrearrange, this));
mLandmarkCombo->setTextChangedCallback(boost::bind(&LLFloaterWorldMap::onComboTextEntry, this));
mZoomSlider = getChild<LLSliderCtrl>("zoom slider");
F32 slider_zoom = mMapView->getZoom();
@ -662,7 +657,6 @@ void LLFloaterWorldMap::draw()
// (!rlv_handler_t::isEnabled()) || !(gRlvHandler.hasBehaviour(RLV_BHVR_TPLM) && gRlvHandler.hasBehaviour(RLV_BHVR_TPLOC)));
//// [/RLVa:KB]
mTeleportButton->setEnabled((bool)tracking_status);
//clear_btn->setEnabled((bool)tracking_status);
mShowDestinationButton->setEnabled((bool)tracking_status || LLWorldMap::getInstance()->isTracking());
mCopySlurlButton->setEnabled((mSLURL.isValid()) );
mGoHomeButton->setEnabled((!rlv_handler_t::isEnabled()) || !(gRlvHandler.hasBehaviour(RLV_BHVR_TPLM) && gRlvHandler.hasBehaviour(RLV_BHVR_TPLOC)));
@ -758,19 +752,17 @@ void LLFloaterWorldMap::requestParcelInfo(const LLVector3d& pos_global, const LL
}
}
void LLFloaterWorldMap::trackAvatar( const LLUUID& avatar_id, const std::string& name )
void LLFloaterWorldMap::trackAvatar(const LLUUID& avatar_id, const std::string& name)
{
mShowParcelInfo = false;
LLCtrlSelectionInterface *iface = childGetSelectionInterface("friend combo");
if (!iface) return;
buildAvatarIDList();
if(iface->setCurrentByID(avatar_id) || gAgent.isGodlike())
if (mFriendCombo->setCurrentByID(avatar_id) || gAgent.isGodlike())
{
// *HACK: Adjust Z values automatically for liaisons & gods so
// they swoop down when they click on the map. Requested
// convenience.
if(gAgent.isGodlike())
if (gAgent.isGodlike())
{
mTeleportCoordSpinZ->setValue(LLSD(200.f));
}
@ -790,43 +782,36 @@ void LLFloaterWorldMap::trackAvatar( const LLUUID& avatar_id, const std::string&
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
}
void LLFloaterWorldMap::trackLandmark( const LLUUID& landmark_item_id )
void LLFloaterWorldMap::trackLandmark(const LLUUID& landmark_item_id)
{
mShowParcelInfo = false;
LLCtrlSelectionInterface *iface = childGetSelectionInterface("landmark combo");
if (!iface) return;
buildLandmarkIDLists();
bool found = false;
S32 idx;
for (idx = 0; idx < mLandmarkItemIDList.size(); idx++)
{
if ( mLandmarkItemIDList.at(idx) == landmark_item_id)
if (mLandmarkItemIDList.at(idx) == landmark_item_id)
{
found = true;
break;
}
}
if (found && iface->setCurrentByID( landmark_item_id ) )
if (found && mLandmarkCombo->setCurrentByID(landmark_item_id))
{
LLUUID asset_id = mLandmarkAssetIDList.at( idx );
std::string name;
LLComboBox* combo = getChild<LLComboBox>( "landmark combo");
if (combo) name = combo->getSimple();
LLUUID asset_id = mLandmarkAssetIDList.at(idx);
std::string name = mLandmarkCombo->getSimple();
mTrackedStatus = LLTracker::TRACKING_LANDMARK;
LLTracker::trackLandmark(mLandmarkAssetIDList.at( idx ), // assetID
mLandmarkItemIDList.at( idx ), // itemID
LLTracker::trackLandmark(mLandmarkAssetIDList.at(idx), // assetID
mLandmarkItemIDList.at(idx), // itemID
name); // name
if( asset_id != sHomeID )
if (asset_id != sHomeID)
{
// start the download process
gLandmarkList.getAsset( asset_id);
gLandmarkList.getAsset(asset_id);
}
// We have to download both region info and landmark data, so set busy. JC
// getWindow()->incBusyCount();
}
else
{
@ -1198,14 +1183,11 @@ void LLFloaterWorldMap::friendsChanged()
LLAvatarTracker& t = LLAvatarTracker::instance();
const LLUUID& avatar_id = t.getAvatarID();
buildAvatarIDList();
if(avatar_id.notNull())
if (avatar_id.notNull())
{
LLCtrlSelectionInterface *iface = childGetSelectionInterface("friend combo");
const LLRelationship* buddy_info = t.getBuddyInfo(avatar_id);
if(!iface ||
!iface->setCurrentByID(avatar_id) ||
(buddy_info && !buddy_info->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION)) ||
gAgent.isGodlike())
if (!mFriendCombo->setCurrentByID(avatar_id) ||
(buddy_info && !buddy_info->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION)) || gAgent.isGodlike())
{
LLTracker::stopTracking(false);
}
@ -1215,15 +1197,12 @@ void LLFloaterWorldMap::friendsChanged()
// No longer really builds a list. Instead, just updates mAvatarCombo.
void LLFloaterWorldMap::buildAvatarIDList()
{
LLCtrlListInterface *list = mListFriendCombo;
if (!list) return;
// Delete all but the "None" entry
S32 list_size = list->getItemCount();
S32 list_size = mFriendCombo->getItemCount();
if (list_size > 1)
{
list->selectItemRange(1, -1);
list->operateOnSelection(LLCtrlListInterface::OP_DELETE);
mFriendCombo->selectItemRange(1, -1);
mFriendCombo->operateOnSelection(LLCtrlListInterface::OP_DELETE);
}
// Get all of the calling cards for avatar that are currently online
@ -1234,38 +1213,35 @@ void LLFloaterWorldMap::buildAvatarIDList()
it = collector.mMappable.begin();
end = collector.mMappable.end();
// <FS:Ansariel> Sort friend list alphabetically
//for( ; it != end; ++it)
//for(; it != end; ++it)
//{
// list->addSimpleElement((*it).second, ADD_BOTTOM, (*it).first);
// mFriendCombo->addSimpleElement((*it).second, ADD_BOTTOM, (*it).first);
//}
std::multimap<std::string, LLUUID> buddymap;
for( ; it != end; ++it)
for(; it != end; ++it)
{
buddymap.insert(std::make_pair((*it).second, (*it).first));
}
for (std::multimap<std::string, LLUUID>::iterator bit = buddymap.begin(); bit != buddymap.end(); ++bit)
{
list->addSimpleElement((*bit).first, ADD_BOTTOM, (*bit).second);
mFriendCombo->addSimpleElement((*bit).first, ADD_BOTTOM, (*bit).second);
}
// </FS:Ansariel>
list->setCurrentByID( LLAvatarTracker::instance().getAvatarID() );
list->selectFirstItem();
mFriendCombo->setCurrentByID(LLAvatarTracker::instance().getAvatarID());
mFriendCombo->selectFirstItem();
}
void LLFloaterWorldMap::buildLandmarkIDLists()
{
LLCtrlListInterface *list = mListLandmarkCombo;
if (!list) return;
// Delete all but the "None" entry
S32 list_size = list->getItemCount();
S32 list_size = mLandmarkCombo->getItemCount();
if (list_size > 1)
{
list->selectItemRange(1, -1);
list->operateOnSelection(LLCtrlListInterface::OP_DELETE);
mLandmarkCombo->selectItemRange(1, -1);
mLandmarkCombo->operateOnSelection(LLCtrlListInterface::OP_DELETE);
}
mLandmarkItemIDList.clear();
@ -1312,13 +1288,13 @@ void LLFloaterWorldMap::buildLandmarkIDLists()
}
// </FS:Ansariel>
list->addSimpleElement(item->getName(), ADD_BOTTOM, item->getUUID());
mLandmarkCombo->addSimpleElement(item->getName(), ADD_BOTTOM, item->getUUID());
mLandmarkAssetIDList.push_back( item->getAssetUUID() );
mLandmarkItemIDList.push_back( item->getUUID() );
}
list->selectFirstItem();
mLandmarkCombo->selectFirstItem();
}
@ -1336,10 +1312,9 @@ F32 LLFloaterWorldMap::getDistanceToDestination(const LLVector3d &destination,
void LLFloaterWorldMap::clearLocationSelection(bool clear_ui, bool dest_reached)
{
LLCtrlListInterface *list = mListSearchResults;
if (list && (!dest_reached || (list->getItemCount() == 1)))
if (!dest_reached || (mSearchResults->getItemCount() == 1))
{
list->operateOnAll(LLCtrlListInterface::OP_DELETE);
mSearchResults->operateOnAll(LLCtrlListInterface::OP_DELETE);
}
LLWorldMap::getInstance()->cancelTracking();
mCompletingRegionName = "";
@ -1350,11 +1325,7 @@ void LLFloaterWorldMap::clearLandmarkSelection(bool clear_ui)
{
if (clear_ui || !childHasKeyboardFocus("landmark combo"))
{
LLCtrlListInterface *list = mListLandmarkCombo;
if (list)
{
list->selectByValue( "None" );
}
mLandmarkCombo->selectByValue("None");
}
}
@ -1364,10 +1335,9 @@ void LLFloaterWorldMap::clearAvatarSelection(bool clear_ui)
if (clear_ui || !childHasKeyboardFocus("friend combo"))
{
mTrackedStatus = LLTracker::TRACKING_NOTHING;
LLCtrlListInterface *list = mListFriendCombo;
if (list && list->getSelectedValue().asString() != "None")
if (mFriendCombo->getSelectedValue().asString() != "None")
{
list->selectByValue( "None" );
mFriendCombo->selectByValue("None");
}
}
}
@ -1423,25 +1393,21 @@ void LLFloaterWorldMap::onGoHome()
}
void LLFloaterWorldMap::onLandmarkComboPrearrange( )
void LLFloaterWorldMap::onLandmarkComboPrearrange()
{
if( mIsClosing )
if (mIsClosing)
{
return;
}
LLCtrlListInterface *list = mListLandmarkCombo;
if (!list) return;
LLUUID current_choice = list->getCurrentID();
LLUUID current_choice = mLandmarkCombo->getCurrentID();
buildLandmarkIDLists();
if( current_choice.isNull() || !list->setCurrentByID( current_choice ) )
if (current_choice.isNull() || !mLandmarkCombo->setCurrentByID(current_choice))
{
LLTracker::stopTracking(false);
}
}
void LLFloaterWorldMap::onComboTextEntry()
@ -1461,33 +1427,28 @@ void LLFloaterWorldMap::onSearchTextEntry( )
void LLFloaterWorldMap::onLandmarkComboCommit()
{
if( mIsClosing )
if (mIsClosing)
{
return;
}
LLCtrlListInterface *list = mListLandmarkCombo;
if (!list) return;
LLUUID asset_id;
LLUUID item_id = list->getCurrentID();
LLUUID item_id = mLandmarkCombo->getCurrentID();
LLTracker::stopTracking(false);
//RN: stopTracking() clears current combobox selection, need to reassert it here
list->setCurrentByID(item_id);
// RN: stopTracking() clears current combobox selection, need to reassert it here
mLandmarkCombo->setCurrentByID(item_id);
if( item_id.isNull() )
{
}
else if( item_id == sHomeID )
if (item_id.isNull()) {}
else if (item_id == sHomeID)
{
asset_id = sHomeID;
}
else
{
LLInventoryItem* item = gInventory.getItem( item_id );
if( item )
LLInventoryItem* item = gInventory.getItem(item_id);
if (item)
{
asset_id = item->getAssetUUID();
}
@ -1498,34 +1459,31 @@ void LLFloaterWorldMap::onLandmarkComboCommit()
}
}
trackLandmark( item_id);
trackLandmark(item_id);
onShowTargetBtn();
// Reset to user postion if nothing is tracked
mSetToUserPosition = ( LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING );
mSetToUserPosition = (LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING);
}
// static
void LLFloaterWorldMap::onAvatarComboPrearrange( )
void LLFloaterWorldMap::onAvatarComboPrearrange()
{
if( mIsClosing )
if (mIsClosing)
{
return;
}
LLCtrlListInterface *list = mListFriendCombo;
if (!list) return;
LLUUID current_choice;
if( LLAvatarTracker::instance().haveTrackingInfo() )
if (LLAvatarTracker::instance().haveTrackingInfo())
{
current_choice = LLAvatarTracker::instance().getAvatarID();
}
buildAvatarIDList();
if( !list->setCurrentByID( current_choice ) || current_choice.isNull() )
if (!mFriendCombo->setCurrentByID(current_choice) || current_choice.isNull())
{
LLTracker::stopTracking(false);
}
@ -1533,26 +1491,21 @@ void LLFloaterWorldMap::onAvatarComboPrearrange( )
void LLFloaterWorldMap::onAvatarComboCommit()
{
if( mIsClosing )
if (mIsClosing)
{
return;
}
LLCtrlListInterface *list = mListFriendCombo;
if (!list) return;
const LLUUID& new_avatar_id = list->getCurrentID();
const LLUUID& new_avatar_id = mFriendCombo->getCurrentID();
if (new_avatar_id.notNull())
{
std::string name;
LLComboBox* combo = getChild<LLComboBox>("friend combo");
if (combo) name = combo->getSimple();
std::string name = mFriendCombo->getSimple();
trackAvatar(new_avatar_id, name);
onShowTargetBtn();
}
else
{ // Reset to user postion if nothing is tracked
mSetToUserPosition = ( LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING );
mSetToUserPosition = (LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING);
}
}
@ -1685,8 +1638,9 @@ void LLFloaterWorldMap::onCopySLURL()
// std::string image_name = getString(toggle_collapse ? "expand_icon" : "collapse_icon");
// std::string tooltip = getString(toggle_collapse ? "expand_tooltip" : "collapse_tooltip");
// getChild<LLIconCtrl>("expand_collapse_icon")->setImage(LLUI::getUIImage(image_name));
// getChild<LLIconCtrl>("expand_collapse_icon")->setToolTip(tooltip);
// LLIconCtrl* expandCollapseIcon = getChild<LLIconCtrl>("expand_collapse_icon");
// expandCollapseIcon->setImage(LLUI::getUIImage(image_name));
// expandCollapseIcon->setToolTip(tooltip);
// getChild<LLPanel>("expand_btn_panel")->setToolTip(tooltip);
//}
// </FS:Ansariel>
@ -1911,9 +1865,9 @@ void LLFloaterWorldMap::teleportToAvatar()
void LLFloaterWorldMap::flyToAvatar()
{
if( LLAvatarTracker::instance().haveTrackingInfo() )
if (LLAvatarTracker::instance().haveTrackingInfo())
{
gAgent.startAutoPilotGlobal( LLAvatarTracker::instance().getGlobalPos() );
gAgent.startAutoPilotGlobal(LLAvatarTracker::instance().getGlobalPos());
}
}
@ -1924,8 +1878,7 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim)
return;
}
LLScrollListCtrl *list = getChild<LLScrollListCtrl>("search_results");
list->operateOnAll(LLCtrlListInterface::OP_DELETE);
mSearchResults->operateOnAll(LLCtrlListInterface::OP_DELETE);
// auto name_length = mCompletingRegionName.length(); // <FS:Beq/> FIRE-23591 support map search partial matches (Patch by Kevin Cozens)
@ -1955,7 +1908,7 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim)
value["id"] = info->getName();
value["columns"][0]["column"] = "sim_name";
value["columns"][0]["value"] = info->getName();
list->addElement(value);
mSearchResults->addElement(value);
num_results++;
}
}
@ -1968,26 +1921,26 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim)
if (num_results > 0)
{
// Ansariel: Let's sort the list to make it more user-friendly
list->sortByColumn("sim_name", true);
mSearchResults->sortByColumn("sim_name", true);
// if match found, highlight it and go
if (!match.isUndefined())
{
list->selectByValue(match);
mSearchResults->selectByValue(match);
}
// else select first found item
else
{
list->selectFirstItem();
mSearchResults->selectFirstItem();
}
getChild<LLUICtrl>("search_results")->setFocus(true);
mSearchResults->setFocus(true);
onCommitSearchResult();
}
else
{
// if we found nothing, say "none"
list->setCommentText(LLTrans::getString("worldmap_results_none_found"));
list->operateOnAll(LLCtrlListInterface::OP_DESELECT);
mSearchResults->setCommentText(LLTrans::getString("worldmap_results_none_found"));
mSearchResults->operateOnAll(LLCtrlListInterface::OP_DESELECT);
}
}
@ -2001,11 +1954,7 @@ void LLFloaterWorldMap::onTeleportFinished()
void LLFloaterWorldMap::onCommitSearchResult()
{
LLCtrlListInterface *list = mListSearchResults;
if (!list) return;
LLSD selected_value = list->getSelectedValue();
std::string sim_name = selected_value.asString();
std::string sim_name = mSearchResults->getSelectedValue().asString();
if (sim_name.empty())
{
return;
@ -2021,7 +1970,7 @@ void LLFloaterWorldMap::onCommitSearchResult()
{
LLVector3d pos_global = info->getGlobalOrigin();
const F64 SIM_COORD_DEFAULT = 128.0;
constexpr F64 SIM_COORD_DEFAULT = 128.0;
LLVector3 pos_local(SIM_COORD_DEFAULT, SIM_COORD_DEFAULT, 0.0f);
// Did this value come from a trackURL() request?

View File

@ -54,6 +54,8 @@ class LLCheckBoxCtrl;
class LLSliderCtrl;
class LLSpinCtrl;
class LLSearchEditor;
class LLComboBox;
class LLScrollListCtrl;
class LLWorldMapParcelInfoObserver : public LLRemoteParcelInfoObserver
{
@ -224,10 +226,6 @@ private:
LLUUID mTrackedAvatarID;
LLSLURL mSLURL;
LLCtrlListInterface * mListFriendCombo;
LLCtrlListInterface * mListLandmarkCombo;
LLCtrlListInterface * mListSearchResults;
LLButton* mTeleportButton = nullptr;
LLButton* mShowDestinationButton = nullptr;
LLButton* mCopySlurlButton = nullptr;
@ -259,6 +257,11 @@ private:
LLSliderCtrl* mZoomSlider = nullptr;
LLComboBox* mLandmarkCombo = nullptr;
LLComboBox* mFriendCombo = nullptr;
LLScrollListCtrl* mSearchResults = nullptr;
LLPanel* mTrackCtrlsPanel = nullptr;
boost::signals2::connection mTeleportFinishConnection;

View File

@ -311,10 +311,6 @@ void LLHUDText::renderText()
}
text_color = segment_iter->mColor;
if (mOnHUDAttachment)
{
text_color = linearColor4(text_color);
}
text_color.mV[VALPHA] *= alpha_factor;
// <FS:minerjr> [FIRE-35019] Add LLHUDNameTag background to floating text and hover highlights
// If the text object is highlighted and use hover highlight is enabled, then reset the alpha factor (1.0f)

View File

@ -1100,6 +1100,7 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag
bool on_screen = false;
U32 face_count = 0;
U32 max_faces_to_check = 1024;
// get adjusted bias based on image resolution
LLImageGL* img = imagep->getGLTexture();
@ -1142,13 +1143,15 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)
{
for (S32 fi = 0; fi < imagep->getNumFaces(i); ++fi)
face_count += imagep->getNumFaces(i);
S32 faces_to_check = (face_count > max_faces_to_check) ? 0 : imagep->getNumFaces(i);
for (S32 fi = 0; fi < faces_to_check; ++fi)
{
LLFace* face = (*(imagep->getFaceList(i)))[fi];
if (face && face->getViewerObject())
{
++face_count;
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
// No longer needed as we no longer re-calculate the face's virtual texture size, we use it directly from the face
//F32 radius;
@ -1259,14 +1262,13 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag
on_screen = bool(on_screen_count);
imagep->setCloseToCamera(close_to_camera > 0.0f ? 1.0f : 0.0f);
//if (face_count > 1024)
//if (face_count > max_faces_to_check)
// Add check for if the image is animated to boost to high as well
if (face_count > 1024 || animated != 0)
if (face_count > max_faces_to_check || animated != 0)
// </FS:minerjr> [FIRE-35081]
{ // this texture is used in so many places we should just boost it and not bother checking its vsize
// this is especially important because the above is not time sliced and can hit multiple ms for a single texture
imagep->setBoostLevel(LLViewerFetchedTexture::BOOST_HIGH);
// Do we ever remove it? This also sets texture nodelete!
max_vsize = MAX_IMAGE_AREA;
}
if (imagep->getType() == LLViewerTexture::LOD_TEXTURE && imagep->getBoostLevel() == LLViewerTexture::BOOST_NONE)

View File

@ -5888,7 +5888,18 @@ void LLViewerWindow::saveImageLocal(LLImageFormatted *image, const snapshot_save
#else
boost::filesystem::path b_path(lastSnapshotDir);
#endif
if (!boost::filesystem::is_directory(b_path))
boost::system::error_code ec;
if (!boost::filesystem::is_directory(b_path, ec) || ec.failed())
{
LLSD args;
args["PATH"] = lastSnapshotDir;
LLNotificationsUtil::add("SnapshotToLocalDirNotExist", args);
resetSnapshotLoc();
failure_cb();
return;
}
boost::filesystem::space_info b_space = boost::filesystem::space(b_path, ec);
if (ec.failed())
{
LLSD args;
args["PATH"] = lastSnapshotDir;
@ -5897,7 +5908,6 @@ void LLViewerWindow::saveImageLocal(LLImageFormatted *image, const snapshot_save
failure_cb();
return;
}
boost::filesystem::space_info b_space = boost::filesystem::space(b_path);
if (b_space.free < image->getDataSize())
{
LLSD args;
@ -5914,6 +5924,8 @@ void LLViewerWindow::saveImageLocal(LLImageFormatted *image, const snapshot_save
LLNotificationsUtil::add("SnapshotToComputerFailed", args);
failure_cb();
// Shouldn't there be a return here?
}
// Look for an unused file name

View File

@ -985,7 +985,10 @@ void LLWebRTCVoiceClient::updatePosition(void)
LLWebRTCVoiceClient::participantStatePtr_t participant = findParticipantByID("Estate", gAgentID);
if(participant)
{
participant->mRegion = gAgent.getRegion()->getRegionID();
if (participant->mRegion != region->getRegionID()) {
participant->mRegion = region->getRegionID();
setMuteMic(mMuteMic);
}
}
}
}
@ -3106,8 +3109,6 @@ LLVoiceWebRTCSpatialConnection::~LLVoiceWebRTCSpatialConnection()
void LLVoiceWebRTCSpatialConnection::setMuteMic(bool muted)
{
if (mMuted != muted)
{
mMuted = muted;
if (mWebRTCAudioInterface)
{
@ -3124,7 +3125,6 @@ void LLVoiceWebRTCSpatialConnection::setMuteMic(bool muted)
mWebRTCAudioInterface->setMute(true);
}
}
}
}
/////////////////////////////