Merge LL V3.7.10

Ansariel 2015-01-11 18:40:22 +01:00
commit 1f465086c0
122 changed files with 8745 additions and 746 deletions

View File

@ -520,3 +520,4 @@ d029faf69f20a23007f32420a1ac6a3b89a6d441 3.7.6-release
83959480cb986522d07b151a0c778ab7f920d41b 3.7.7-release
bba9b3722eea08949e4ff69591f736bf0f808434 3.7.8-release
a9f2d0cb11f73b06858e6083bb50083becc3f9cd 3.7.9-release
91dae9494b4d147541c7a01902334ba19a7ec05e 3.7.10-release

View File

@ -7,6 +7,7 @@ project (llimage_libtest)
include(00-Common)
include(LLCommon)
include(LLImage)
include(LLMath)
include(LLImageJ2COJ)
include(LLKDU)
include(LLVFS)
@ -15,6 +16,7 @@ include_directories(
${LLCOMMON_INCLUDE_DIRS}
${LLVFS_INCLUDE_DIRS}
${LLIMAGE_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
)
include_directories(SYSTEM
${LLCOMMON_SYSTEM_INCLUDE_DIRS}
@ -64,6 +66,7 @@ endif (DARWIN)
target_link_libraries(llimage_libtest
${LLCOMMON_LIBRARIES}
${LLVFS_LIBRARIES}
${LLMATH_LIBRARIES}
${LLIMAGE_LIBRARIES}
${LLKDU_LIBRARIES}
${KDU_LIBRARY}

View File

@ -0,0 +1,41 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.1</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>contrast</string>
<real>0.8</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.5</real>
<real>0.0</real>
<real>0.0</real>
</array>
<array>
<string>blend</string>
<real>10.0</real>
<real>0.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.1</real>
<real>0.1</real>
<real>0.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,11 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.01</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,36 @@
<llsd>
<array>
<array>
<string>grayscale</string>
</array>
<array>
<string>linearize</string>
<real>0.1</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>posterize</string>
<real>10.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>gradient</string>
</array>
<array>
<string>colorize</string>
<real>0.0</real>
<real>0.0</real>
<real>1.0</real>
<real>0.0</real>
<real>0.0</real>
<real>0.15</real>
</array>
<array>
<string>blur</string>
</array>
</array>
</llsd>

View File

@ -0,0 +1,25 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>stencil</string>
<string>uniform</string>
<string>add</string>
<real>0.0</real>
<real>1.0</real>
</array>
<array>
<string>gamma</string>
<real>0.25</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,7 @@
<llsd>
<array>
<array>
<string>blur</string>
</array>
</array>
</llsd>

View File

@ -0,0 +1,11 @@
<llsd>
<array>
<array>
<string>brighten</string>
<real>0.5</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,24 @@
<llsd>
<array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>blend</string>
<real>0.0</real>
<real>1.0</real>
<real>0.0</real>
<real>0.0</real>
<real>1.0</real>
<real>10.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>0.0</real>
<real>0.0</real>
<real>0.5</real>
<real>0.5</real>
<real>0.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,16 @@
<llsd>
<array>
<array>
<string>colortransform</string>
<real>0.2125</real>
<real>0.7154</real>
<real>0.0721</real>
<real>0.2125</real>
<real>0.7154</real>
<real>0.0721</real>
<real>0.2125</real>
<real>0.7154</real>
<real>0.0721</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,11 @@
<llsd>
<array>
<array>
<string>contrast</string>
<real>1.5</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,18 @@
<llsd>
<array>
<array>
<string>convolve</string>
<real>1.0</real>
<real>0.0</real>
<real>4.0</real>
<real>1.0</real>
<real>4.0</real>
<real>1.0</real>
<real>0.0</real>
<real>1.0</real>
<real>4.0</real>
<real>1.0</real>
<real>4.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,11 @@
<llsd>
<array>
<array>
<string>darken</string>
<real>0.5</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,47 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>add</string>
<real>0.0</real>
<real>0.4</real>
<real>0.0</real>
<real>0.0</real>
<real>1.0</real>
<real>2.0</real>
</array>
<array>
<string>contrast</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>add</string>
<real>-0.8</real>
<real>0.0</real>
<real>0.0</real>
<real>0.0</real>
<real>1.0</real>
<real>2.0</real>
</array>
<array>
<string>contrast</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,24 @@
<llsd>
<array>
<array>
<string>gradient</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>linearize</string>
<real>0.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>contrast</string>
<real>2.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,39 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>blend</string>
<real>0.0</real>
<real>0.4</real>
<real>0.0</real>
<real>0.0</real>
<real>0.5</real>
<real>2.0</real>
</array>
<array>
<string>sharpen</string>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>blend</string>
<real>1.0</real>
<real>0.0</real>
<real>0.0</real>
<real>0.0</real>
<real>0.5</real>
<real>2.0</real>
</array>
<array>
<string>blur</string>
</array>
</array>
</llsd>

View File

@ -0,0 +1,11 @@
<llsd>
<array>
<array>
<string>gamma</string>
<real>1.7</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,14 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>grayscale</string>
</array>
</array>
</llsd>

View File

@ -0,0 +1,38 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.1</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>contrast</string>
<real>0.8</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>fade</string>
<real>0.5</real>
<real>1.0</real>
<real>0.0</real>
<real>0.0</real>
<real>1.0</real>
<real>4.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>0.0</real>
<real>1.0</real>
<real>0.4</real>
<real>0.0</real>
<real>0.2</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,20 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>grayscale</string>
</array>
<array>
<string>screen</string>
<string>line</string>
<real>0.015</real>
<real>0.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,20 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.1</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>grayscale</string>
</array>
<array>
<string>screen</string>
<string>line</string>
<real>0.02</real>
<real>0.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,131 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.01</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>stencil</string>
<string>gradient</string>
<string>add</string>
<real>1.0</real>
<real>0.0</real>
<real>-1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>-1.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.1</real>
<real>0.1</real>
<real>0.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>add</string>
<real>0.0</real>
<real>1.0</real>
<real>-1.0</real>
<real>1.0</real>
<real>1.5</real>
<real>5.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.6</real>
<real>0.0</real>
<real>0.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>add</string>
<real>0.0</real>
<real>1.0</real>
<real>-1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>5.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.6</real>
<real>0.6</real>
<real>0.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>add</string>
<real>0.0</real>
<real>0.5</real>
<real>0.5</real>
<real>-0.5</real>
<real>0.10</real>
<real>20.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.7</real>
<real>0.0</real>
<real>0.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>add</string>
<real>0.0</real>
<real>0.5</real>
<real>0.6</real>
<real>-0.6</real>
<real>0.05</real>
<real>20.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.7</real>
<real>0.0</real>
<real>0.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>add</string>
<real>0.0</real>
<real>0.5</real>
<real>0.4</real>
<real>-0.4</real>
<real>0.025</real>
<real>20.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.7</real>
<real>0.0</real>
<real>0.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,78 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.01</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>brighten</string>
<real>0.1</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>stencil</string>
<string>gradient</string>
<string>add</string>
<real>1.0</real>
<real>0.0</real>
<real>-1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>-1.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.1</real>
<real>0.1</real>
<real>0.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>add</string>
<real>0.0</real>
<real>1.0</real>
<real>-1.0</real>
<real>1.0</real>
<real>1.5</real>
<real>5.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.8</real>
<real>0.0</real>
<real>0.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>add</string>
<real>0.0</real>
<real>1.0</real>
<real>-1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>5.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.8</real>
<real>0.8</real>
<real>0.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,11 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.1</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,118 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.02</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>contrast</string>
<real>1.02</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>saturate</string>
<real>1.2</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>blend</string>
<real>0.0</real>
<real>0.25</real>
<real>0.0</real>
<real>0.0</real>
<real>0.25</real>
<real>2.0</real>
</array>
<array>
<string>sharpen</string>
</array>
<array>
<string>stencil</string>
<string>gradient</string>
<string>blend</string>
<real>1.0</real>
<real>0.0</real>
<real>0.0</real>
<real>-1.0</real>
<real>0.0</real>
<real>-0.25</real>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>stencil</string>
<string>gradient</string>
<string>blend</string>
<real>1.0</real>
<real>0.0</real>
<real>0.0</real>
<real>1.0</real>
<real>0.0</real>
<real>0.25</real>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
</array>
</llsd>

View File

@ -0,0 +1,20 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>grayscale</string>
</array>
<array>
<string>screen</string>
<string>2Dsine</string>
<real>0.015</real>
<real>0.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,24 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.1</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.0</real>
<real>0.3</real>
<real>0.0</real>
</array>
<array>
<string>saturate</string>
<real>0.35</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,35 @@
<llsd>
<array>
<array>
<string>blur</string>
</array>
<array>
<string>darken</string>
<real>0.1</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>contrast</string>
<real>0.9</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>linearize</string>
<real>0.01</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>posterize</string>
<real>4.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,11 @@
<llsd>
<array>
<array>
<string>posterize</string>
<real>10.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,8 @@
<llsd>
<array>
<array>
<string>rotate</string>
<real>180.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,8 @@
<llsd>
<array>
<array>
<string>saturate</string>
<real>3.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,14 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>sepia</string>
</array>
</array>
</llsd>

View File

@ -0,0 +1,7 @@
<llsd>
<array>
<array>
<string>sharpen</string>
</array>
</array>
</llsd>

View File

@ -0,0 +1,20 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>grayscale</string>
</array>
<array>
<string>screen</string>
<string>line</string>
<real>0.015</real>
<real>45.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,45 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.1</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>contrast</string>
<real>0.8</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>saturate</string>
<real>1.5</real>
</array>
<array>
<string>fade</string>
<real>1.0</real>
<real>0.25</real>
</array>
<array>
<string>saturate</string>
<real>0.8</real>
</array>
<array>
<string>contrast</string>
<real>1.1</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>brighten</string>
<real>30</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,24 @@
<llsd>
<array>
<array>
<string>stencil</string>
<string>gradient</string>
<string>blend</string>
<real>0.0</real>
<real>1.0</real>
<real>0.0</real>
<real>-1.0</real>
<real>0.0</real>
<real>1.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>0.0</real>
<real>0.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,22 @@
<llsd>
<array>
<array>
<string>stencil</string>
<string>scanlines</string>
<string>blend</string>
<real>0.0</real>
<real>0.5</real>
<real>0.1</real>
<real>45.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>0.0</real>
<real>0.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,20 @@
<llsd>
<array>
<array>
<string>stencil</string>
<string>uniform</string>
<string>blend</string>
<real>0.0</real>
<real>0.5</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>0.0</real>
<real>0.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,24 @@
<llsd>
<array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>blend</string>
<real>0.0</real>
<real>0.5</real>
<real>0.0</real>
<real>0.0</real>
<real>1.0</real>
<real>10.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>0.0</real>
<real>0.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,42 @@
<llsd>
<array>
<array>
<string>grayscale</string>
</array>
<array>
<string>linearize</string>
<real>0.1</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>posterize</string>
<real>50.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>gradient</string>
</array>
<array>
<string>screen</string>
<string>line</string>
<real>0.025</real>
<real>90.0</real>
</array>
<array>
<string>colorize</string>
<real>0.0</real>
<real>1.0</real>
<real>0.0</real>
<real>0.1</real>
<real>0.2</real>
<real>0.2</real>
</array>
<array>
<string>blur</string>
</array>
</array>
</llsd>

View File

@ -0,0 +1,46 @@
<llsd>
<array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>fade</string>
<real>0.0</real>
<real>1.0</real>
<real>0.0</real>
<real>0.0</real>
<real>1.2</real>
<real>3.0</real>
</array>
<array>
<string>linearize</string>
<real>0.05</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>grayscale</string>
</array>
<array>
<string>contrast</string>
<real>1.1</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>blend</string>
<real>1.0</real>
<real>0.0</real>
<real>0.0</real>
<real>0.0</real>
<real>0.5</real>
<real>2.0</real>
</array>
<array>
<string>blur</string>
</array>
</array>
</llsd>

View File

@ -0,0 +1,20 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>grayscale</string>
</array>
<array>
<string>screen</string>
<string>line</string>
<real>0.015</real>
<real>90.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,44 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>darken</string>
<real>0.15</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>stencil</string>
<string>uniform</string>
<string>add</string>
<real>0.0</real>
<real>0.5</real>
</array>
<array>
<string>screen</string>
<string>line</string>
<real>0.02</real>
<real>0.0</real>
</array>
<array>
<string>gamma</string>
<real>0.25</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
</array>
</llsd>

View File

@ -32,6 +32,7 @@
// Linden library includes
#include "llimage.h"
#include "llimagefilter.h"
#include "llimagejpeg.h"
#include "llimagepng.h"
#include "llimagebmp.h"
@ -39,6 +40,8 @@
#include "llimagej2c.h"
#include "lldir.h"
#include "lldiriterator.h"
#include "v4coloru.h"
#include "llsdserialize.h"
// system libraries
#include <iostream>
@ -83,6 +86,8 @@ static const char USAGE[] = "\n"
" -rev, --reversible\n"
" Set the compression to be lossless (reversible in j2c parlance).\n"
" Only valid for output j2c images.\n"
" -f, --filter <file>\n"
" Apply the filter <file> to the input images.\n"
" -log, --logmetrics <metric>\n"
" Log performance data for <metric>. Results in <metric>.slp\n"
" Note: so far, only ImageCompressionTester has been tested.\n"
@ -99,7 +104,7 @@ static bool sAllDone = false;
// Create an empty formatted image instance of the correct type from the filename
LLPointer<LLImageFormatted> create_image(const std::string &filename)
{
std::string exten = gDirUtilp->getExtension(filename);
std::string exten = gDirUtilp->getExtension(filename);
LLPointer<LLImageFormatted> image = LLImageFormatted::createFromExtension(exten);
return image;
}
@ -350,6 +355,7 @@ int main(int argc, char** argv)
int blocks_size = -1;
int levels = 0;
bool reversible = false;
std::string filter_name = "";
// Init whatever is necessary
ll_init_apr();
@ -523,7 +529,26 @@ int main(int argc, char** argv)
break;
}
}
else if (!strcmp(argv[arg], "--analyzeperformance") || !strcmp(argv[arg], "-a"))
else if (!strcmp(argv[arg], "--filter") || !strcmp(argv[arg], "-f"))
{
// '--filter' needs to be specified with a named filter argument
if ((arg + 1) < argc)
{
filter_name = argv[arg+1];
}
if (((arg + 1) >= argc) || (filter_name[0] == '-'))
{
// We don't have an argument left in the arg list or the next argument is another option
std::cout << "No --filter argument given, no filter will be applied" << std::endl;
}
else
{
arg += 1; // Skip that arg now we know it's a valid test name
if ((arg + 1) == argc) // Break out of the loop if we reach the end of the arg list
break;
}
}
else if (!strcmp(argv[arg], "--analyzeperformance") || !strcmp(argv[arg], "-a"))
{
analyze_performance = true;
}
@ -553,7 +578,10 @@ int main(int argc, char** argv)
fast_timer_log_thread = new LogThread(LLFastTimer::sLogName);
fast_timer_log_thread->start();
}
// Load the filter once and for all
LLImageFilter filter(filter_name);
// Perform action on each input file
std::list<std::string>::iterator in_file = input_filenames.begin();
std::list<std::string>::iterator out_file = output_filenames.begin();
@ -568,7 +596,10 @@ int main(int argc, char** argv)
std::cout << "Error: Image " << *in_file << " could not be loaded" << std::endl;
continue;
}
// Apply the filter
filter.executeFilter(raw_image);
// Save file
if (out_file != out_end)
{

View File

@ -27,6 +27,7 @@ set(llimage_SOURCE_FILES
llimage.cpp
llimagedimensionsinfo.cpp
llimagedxt.cpp
llimagefilter.cpp
llimagej2c.cpp
llimagejpeg.cpp
llimagepng.cpp
@ -42,6 +43,7 @@ set(llimage_HEADER_FILES
llimagebmp.h
llimagedimensionsinfo.h
llimagedxt.h
llimagefilter.h
llimagej2c.h
llimagejpeg.h
llimagepng.h

View File

@ -471,18 +471,8 @@ void LLImageRaw::verticalFlip()
void LLImageRaw::expandToPowerOfTwo(S32 max_dim, BOOL scale_image)
{
// Find new sizes
S32 new_width = MIN_IMAGE_SIZE;
S32 new_height = MIN_IMAGE_SIZE;
while( (new_width < getWidth()) && (new_width < max_dim) )
{
new_width <<= 1;
}
while( (new_height < getHeight()) && (new_height < max_dim) )
{
new_height <<= 1;
}
S32 new_width = expandDimToPowerOfTwo(getWidth(), max_dim);
S32 new_height = expandDimToPowerOfTwo(getHeight(), max_dim);
scale( new_width, new_height, scale_image );
}
@ -490,55 +480,61 @@ void LLImageRaw::expandToPowerOfTwo(S32 max_dim, BOOL scale_image)
void LLImageRaw::contractToPowerOfTwo(S32 max_dim, BOOL scale_image)
{
// Find new sizes
S32 new_width = max_dim;
S32 new_height = max_dim;
while( (new_width > getWidth()) && (new_width > MIN_IMAGE_SIZE) )
{
new_width >>= 1;
}
while( (new_height > getHeight()) && (new_height > MIN_IMAGE_SIZE) )
{
new_height >>= 1;
}
S32 new_width = contractDimToPowerOfTwo(getWidth(), MIN_IMAGE_SIZE);
S32 new_height = contractDimToPowerOfTwo(getHeight(), MIN_IMAGE_SIZE);
scale( new_width, new_height, scale_image );
}
void LLImageRaw::biasedScaleToPowerOfTwo(S32 max_dim)
// static
S32 LLImageRaw::biasedDimToPowerOfTwo(S32 curr_dim, S32 max_dim)
{
// Strong bias towards rounding down (to save bandwidth)
// No bias would mean THRESHOLD == 1.5f;
const F32 THRESHOLD = 1.75f;
const F32 THRESHOLD = 1.75f;
// Find new sizes
S32 larger_w = max_dim; // 2^n >= mWidth
S32 smaller_w = max_dim; // 2^(n-1) <= mWidth
while( (smaller_w > getWidth()) && (smaller_w > MIN_IMAGE_SIZE) )
S32 larger_dim = max_dim; // 2^n >= curr_dim
S32 smaller_dim = max_dim; // 2^(n-1) <= curr_dim
while( (smaller_dim > curr_dim) && (smaller_dim > MIN_IMAGE_SIZE) )
{
larger_w = smaller_w;
smaller_w >>= 1;
larger_dim = smaller_dim;
smaller_dim >>= 1;
}
S32 new_width = ( (F32)getWidth() / smaller_w > THRESHOLD ) ? larger_w : smaller_w;
return ( ((F32)curr_dim / (F32)smaller_dim) > THRESHOLD ) ? larger_dim : smaller_dim;
}
S32 larger_h = max_dim; // 2^m >= mHeight
S32 smaller_h = max_dim; // 2^(m-1) <= mHeight
while( (smaller_h > getHeight()) && (smaller_h > MIN_IMAGE_SIZE) )
// static
S32 LLImageRaw::expandDimToPowerOfTwo(S32 curr_dim, S32 max_dim)
{
S32 new_dim = MIN_IMAGE_SIZE;
while( (new_dim < curr_dim) && (new_dim < max_dim) )
{
larger_h = smaller_h;
smaller_h >>= 1;
new_dim <<= 1;
}
S32 new_height = ( (F32)getHeight() / smaller_h > THRESHOLD ) ? larger_h : smaller_h;
return new_dim;
}
// static
S32 LLImageRaw::contractDimToPowerOfTwo(S32 curr_dim, S32 min_dim)
{
S32 new_dim = MAX_IMAGE_SIZE;
while( (new_dim > curr_dim) && (new_dim > min_dim) )
{
new_dim >>= 1;
}
return new_dim;
}
void LLImageRaw::biasedScaleToPowerOfTwo(S32 max_dim)
{
// Find new sizes
S32 new_width = biasedDimToPowerOfTwo(getWidth(),max_dim);
S32 new_height = biasedDimToPowerOfTwo(getHeight(),max_dim);
scale( new_width, new_height );
}
// Calculates (U8)(255*(a/255.f)*(b/255.f) + 0.5f). Thanks, Jim Blinn!
inline U8 LLImageRaw::fastFractionalMult( U8 a, U8 b )
{

View File

@ -216,6 +216,9 @@ public:
void verticalFlip();
static S32 biasedDimToPowerOfTwo(S32 curr_dim, S32 max_dim = MAX_IMAGE_SIZE);
static S32 expandDimToPowerOfTwo(S32 curr_dim, S32 max_dim = MAX_IMAGE_SIZE);
static S32 contractDimToPowerOfTwo(S32 curr_dim, S32 min_dim = MIN_IMAGE_SIZE);
void expandToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE, BOOL scale_image = TRUE);
void contractToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE, BOOL scale_image = TRUE);
void biasedScaleToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE);

939
indra/llimage/llimagefilter.cpp Executable file
View File

@ -0,0 +1,939 @@
/**
* @file llimagefilter.cpp
* @brief Simple Image Filtering. See https://wiki.lindenlab.com/wiki/SL_Viewer_Image_Filters for complete documentation.
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2014, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llimagefilter.h"
#include "llmath.h"
#include "v3color.h"
#include "v4coloru.h"
#include "m3math.h"
#include "v3math.h"
#include "llsdserialize.h"
#include "llstring.h"
//---------------------------------------------------------------------------
// LLImageFilter
//---------------------------------------------------------------------------
LLImageFilter::LLImageFilter(const std::string& file_path) :
mFilterData(LLSD::emptyArray()),
mImage(NULL),
mHistoRed(NULL),
mHistoGreen(NULL),
mHistoBlue(NULL),
mHistoBrightness(NULL),
mStencilBlendMode(STENCIL_BLEND_MODE_BLEND),
mStencilShape(STENCIL_SHAPE_UNIFORM),
mStencilGamma(1.0),
mStencilMin(0.0),
mStencilMax(1.0)
{
// Load filter description from file
llifstream filter_xml(file_path);
if (filter_xml.is_open())
{
// Load and parse the file
LLPointer<LLSDParser> parser = new LLSDXMLParser();
parser->parse(filter_xml, mFilterData, LLSDSerialize::SIZE_UNLIMITED);
filter_xml.close();
}
}
LLImageFilter::~LLImageFilter()
{
mImage = NULL;
ll_aligned_free_16(mHistoRed);
ll_aligned_free_16(mHistoGreen);
ll_aligned_free_16(mHistoBlue);
ll_aligned_free_16(mHistoBrightness);
}
/*
*TODO
* Rename stencil to mask
* Improve perf: use LUT for alpha blending in uniform case
* Add gradient coloring as a filter
*/
//============================================================================
// Apply the filter data to the image passed as parameter
//============================================================================
void LLImageFilter::executeFilter(LLPointer<LLImageRaw> raw_image)
{
mImage = raw_image;
//std::cout << "Filter : size = " << mFilterData.size() << std::endl;
for (S32 i = 0; i < mFilterData.size(); ++i)
{
std::string filter_name = mFilterData[i][0].asString();
// Dump out the filter values (for debug)
//std::cout << "Filter : name = " << mFilterData[i][0].asString() << ", params = ";
//for (S32 j = 1; j < mFilterData[i].size(); ++j)
//{
// std::cout << mFilterData[i][j].asString() << ", ";
//}
//std::cout << std::endl;
if (filter_name == "stencil")
{
// Get the shape of the stencil, that is how the procedural alpha is computed geometrically
std::string filter_shape = mFilterData[i][1].asString();
EStencilShape shape = STENCIL_SHAPE_UNIFORM;
if (filter_shape == "uniform")
{
shape = STENCIL_SHAPE_UNIFORM;
}
else if (filter_shape == "gradient")
{
shape = STENCIL_SHAPE_GRADIENT;
}
else if (filter_shape == "vignette")
{
shape = STENCIL_SHAPE_VIGNETTE;
}
else if (filter_shape == "scanlines")
{
shape = STENCIL_SHAPE_SCAN_LINES;
}
// Get the blend mode of the stencil, that is how the effect is blended in the background through the stencil
std::string filter_mode = mFilterData[i][2].asString();
EStencilBlendMode mode = STENCIL_BLEND_MODE_BLEND;
if (filter_mode == "blend")
{
mode = STENCIL_BLEND_MODE_BLEND;
}
else if (filter_mode == "add")
{
mode = STENCIL_BLEND_MODE_ADD;
}
else if (filter_mode == "add_back")
{
mode = STENCIL_BLEND_MODE_ABACK;
}
else if (filter_mode == "fade")
{
mode = STENCIL_BLEND_MODE_FADE;
}
// Get the float params: mandatory min, max then the optional parameters (4 max)
F32 min = (F32)(mFilterData[i][3].asReal());
F32 max = (F32)(mFilterData[i][4].asReal());
F32 params[4] = {0.0, 0.0, 0.0, 0.0};
for (S32 j = 5; (j < mFilterData[i].size()) && (j < 9); j++)
{
params[j-5] = (F32)(mFilterData[i][j].asReal());
}
// Set the stencil
setStencil(shape,mode,min,max,params);
}
else if (filter_name == "sepia")
{
filterSepia();
}
else if (filter_name == "grayscale")
{
filterGrayScale();
}
else if (filter_name == "saturate")
{
filterSaturate((float)(mFilterData[i][1].asReal()));
}
else if (filter_name == "rotate")
{
filterRotate((float)(mFilterData[i][1].asReal()));
}
else if (filter_name == "gamma")
{
LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
filterGamma((float)(mFilterData[i][1].asReal()),color);
}
else if (filter_name == "colorize")
{
LLColor3 color((float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()));
LLColor3 alpha((F32)(mFilterData[i][4].asReal()),(float)(mFilterData[i][5].asReal()),(float)(mFilterData[i][6].asReal()));
filterColorize(color,alpha);
}
else if (filter_name == "contrast")
{
LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
filterContrast((float)(mFilterData[i][1].asReal()),color);
}
else if (filter_name == "brighten")
{
LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
filterBrightness((float)(mFilterData[i][1].asReal()),color);
}
else if (filter_name == "darken")
{
LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
filterBrightness((float)(-mFilterData[i][1].asReal()),color);
}
else if (filter_name == "linearize")
{
LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
filterLinearize((float)(mFilterData[i][1].asReal()),color);
}
else if (filter_name == "posterize")
{
LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
filterEqualize((S32)(mFilterData[i][1].asReal()),color);
}
else if (filter_name == "screen")
{
std::string screen_name = mFilterData[i][1].asString();
EScreenMode mode = SCREEN_MODE_2DSINE;
if (screen_name == "2Dsine")
{
mode = SCREEN_MODE_2DSINE;
}
else if (screen_name == "line")
{
mode = SCREEN_MODE_LINE;
}
filterScreen(mode,(F32)(mFilterData[i][2].asReal()),(F32)(mFilterData[i][3].asReal()));
}
else if (filter_name == "blur")
{
LLMatrix3 kernel;
for (S32 i = 0; i < NUM_VALUES_IN_MAT3; i++)
for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
kernel.mMatrix[i][j] = 1.0;
convolve(kernel,true,false);
}
else if (filter_name == "sharpen")
{
LLMatrix3 kernel;
for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
kernel.mMatrix[k][j] = -1.0;
kernel.mMatrix[1][1] = 9.0;
convolve(kernel,false,false);
}
else if (filter_name == "gradient")
{
LLMatrix3 kernel;
for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
kernel.mMatrix[k][j] = -1.0;
kernel.mMatrix[1][1] = 8.0;
convolve(kernel,false,true);
}
else if (filter_name == "convolve")
{
LLMatrix3 kernel;
S32 index = 1;
bool normalize = (mFilterData[i][index++].asReal() > 0.0);
bool abs_value = (mFilterData[i][index++].asReal() > 0.0);
for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
kernel.mMatrix[k][j] = mFilterData[i][index++].asReal();
convolve(kernel,normalize,abs_value);
}
else if (filter_name == "colortransform")
{
LLMatrix3 transform;
S32 index = 1;
for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
transform.mMatrix[k][j] = mFilterData[i][index++].asReal();
transform.transpose();
colorTransform(transform);
}
else
{
llwarns << "Filter unknown, cannot execute filter command : " << filter_name << llendl;
}
}
}
//============================================================================
// Filter Primitives
//============================================================================
void LLImageFilter::blendStencil(F32 alpha, U8* pixel, U8 red, U8 green, U8 blue)
{
F32 inv_alpha = 1.0 - alpha;
switch (mStencilBlendMode)
{
case STENCIL_BLEND_MODE_BLEND:
// Classic blend of incoming color with the background image
pixel[VRED] = inv_alpha * pixel[VRED] + alpha * red;
pixel[VGREEN] = inv_alpha * pixel[VGREEN] + alpha * green;
pixel[VBLUE] = inv_alpha * pixel[VBLUE] + alpha * blue;
break;
case STENCIL_BLEND_MODE_ADD:
// Add incoming color to the background image
pixel[VRED] = llclampb(pixel[VRED] + alpha * red);
pixel[VGREEN] = llclampb(pixel[VGREEN] + alpha * green);
pixel[VBLUE] = llclampb(pixel[VBLUE] + alpha * blue);
break;
case STENCIL_BLEND_MODE_ABACK:
// Add back background image to the incoming color
pixel[VRED] = llclampb(inv_alpha * pixel[VRED] + red);
pixel[VGREEN] = llclampb(inv_alpha * pixel[VGREEN] + green);
pixel[VBLUE] = llclampb(inv_alpha * pixel[VBLUE] + blue);
break;
case STENCIL_BLEND_MODE_FADE:
// Fade incoming color to black
pixel[VRED] = alpha * red;
pixel[VGREEN] = alpha * green;
pixel[VBLUE] = alpha * blue;
break;
}
}
void LLImageFilter::colorCorrect(const U8* lut_red, const U8* lut_green, const U8* lut_blue)
{
const S32 components = mImage->getComponents();
llassert( components >= 1 && components <= 4 );
S32 width = mImage->getWidth();
S32 height = mImage->getHeight();
U8* dst_data = mImage->getData();
for (S32 j = 0; j < height; j++)
{
for (S32 i = 0; i < width; i++)
{
// Blend LUT value
blendStencil(getStencilAlpha(i,j), dst_data, lut_red[dst_data[VRED]], lut_green[dst_data[VGREEN]], lut_blue[dst_data[VBLUE]]);
dst_data += components;
}
}
}
void LLImageFilter::colorTransform(const LLMatrix3 &transform)
{
const S32 components = mImage->getComponents();
llassert( components >= 1 && components <= 4 );
S32 width = mImage->getWidth();
S32 height = mImage->getHeight();
U8* dst_data = mImage->getData();
for (S32 j = 0; j < height; j++)
{
for (S32 i = 0; i < width; i++)
{
// Compute transform
LLVector3 src((F32)(dst_data[VRED]),(F32)(dst_data[VGREEN]),(F32)(dst_data[VBLUE]));
LLVector3 dst = src * transform;
dst.clamp(0.0f,255.0f);
// Blend result
blendStencil(getStencilAlpha(i,j), dst_data, dst.mV[VRED], dst.mV[VGREEN], dst.mV[VBLUE]);
dst_data += components;
}
}
}
void LLImageFilter::convolve(const LLMatrix3 &kernel, bool normalize, bool abs_value)
{
const S32 components = mImage->getComponents();
llassert( components >= 1 && components <= 4 );
// Compute normalization factors
F32 kernel_min = 0.0;
F32 kernel_max = 0.0;
for (S32 i = 0; i < NUM_VALUES_IN_MAT3; i++)
{
for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
{
if (kernel.mMatrix[i][j] >= 0.0)
kernel_max += kernel.mMatrix[i][j];
else
kernel_min += kernel.mMatrix[i][j];
}
}
if (abs_value)
{
kernel_max = llabs(kernel_max);
kernel_min = llabs(kernel_min);
kernel_max = llmax(kernel_max,kernel_min);
kernel_min = 0.0;
}
F32 kernel_range = kernel_max - kernel_min;
// Allocate temporary buffers and initialize algorithm's data
S32 width = mImage->getWidth();
S32 height = mImage->getHeight();
U8* dst_data = mImage->getData();
S32 buffer_size = width * components;
llassert_always(buffer_size > 0);
std::vector<U8> even_buffer(buffer_size);
std::vector<U8> odd_buffer(buffer_size);
U8* south_data = dst_data + buffer_size;
U8* east_west_data;
U8* north_data;
// Line 0 : we set the line to 0 (debatable)
memcpy( &even_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */
for (S32 i = 0; i < width; i++)
{
blendStencil(getStencilAlpha(i,0), dst_data, 0, 0, 0);
dst_data += components;
}
south_data += buffer_size;
// All other lines
for (S32 j = 1; j < (height-1); j++)
{
// We need to buffer 2 lines. We flip north and east-west (current) to avoid moving too much memory around
if (j % 2)
{
memcpy( &odd_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */
east_west_data = &odd_buffer[0];
north_data = &even_buffer[0];
}
else
{
memcpy( &even_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */
east_west_data = &even_buffer[0];
north_data = &odd_buffer[0];
}
// First pixel : set to 0
blendStencil(getStencilAlpha(0,j), dst_data, 0, 0, 0);
dst_data += components;
// Set pointers to kernel
U8* NW = north_data;
U8* N = NW+components;
U8* NE = N+components;
U8* W = east_west_data;
U8* C = W+components;
U8* E = C+components;
U8* SW = south_data;
U8* S = SW+components;
U8* SE = S+components;
// All other pixels
for (S32 i = 1; i < (width-1); i++)
{
// Compute convolution
LLVector3 dst;
dst.mV[VRED] = (kernel.mMatrix[0][0]*NW[VRED] + kernel.mMatrix[0][1]*N[VRED] + kernel.mMatrix[0][2]*NE[VRED] +
kernel.mMatrix[1][0]*W[VRED] + kernel.mMatrix[1][1]*C[VRED] + kernel.mMatrix[1][2]*E[VRED] +
kernel.mMatrix[2][0]*SW[VRED] + kernel.mMatrix[2][1]*S[VRED] + kernel.mMatrix[2][2]*SE[VRED]);
dst.mV[VGREEN] = (kernel.mMatrix[0][0]*NW[VGREEN] + kernel.mMatrix[0][1]*N[VGREEN] + kernel.mMatrix[0][2]*NE[VGREEN] +
kernel.mMatrix[1][0]*W[VGREEN] + kernel.mMatrix[1][1]*C[VGREEN] + kernel.mMatrix[1][2]*E[VGREEN] +
kernel.mMatrix[2][0]*SW[VGREEN] + kernel.mMatrix[2][1]*S[VGREEN] + kernel.mMatrix[2][2]*SE[VGREEN]);
dst.mV[VBLUE] = (kernel.mMatrix[0][0]*NW[VBLUE] + kernel.mMatrix[0][1]*N[VBLUE] + kernel.mMatrix[0][2]*NE[VBLUE] +
kernel.mMatrix[1][0]*W[VBLUE] + kernel.mMatrix[1][1]*C[VBLUE] + kernel.mMatrix[1][2]*E[VBLUE] +
kernel.mMatrix[2][0]*SW[VBLUE] + kernel.mMatrix[2][1]*S[VBLUE] + kernel.mMatrix[2][2]*SE[VBLUE]);
if (abs_value)
{
dst.mV[VRED] = llabs(dst.mV[VRED]);
dst.mV[VGREEN] = llabs(dst.mV[VGREEN]);
dst.mV[VBLUE] = llabs(dst.mV[VBLUE]);
}
if (normalize)
{
dst.mV[VRED] = (dst.mV[VRED] - kernel_min)/kernel_range;
dst.mV[VGREEN] = (dst.mV[VGREEN] - kernel_min)/kernel_range;
dst.mV[VBLUE] = (dst.mV[VBLUE] - kernel_min)/kernel_range;
}
dst.clamp(0.0f,255.0f);
// Blend result
blendStencil(getStencilAlpha(i,j), dst_data, dst.mV[VRED], dst.mV[VGREEN], dst.mV[VBLUE]);
// Next pixel
dst_data += components;
NW += components;
N += components;
NE += components;
W += components;
C += components;
E += components;
SW += components;
S += components;
SE += components;
}
// Last pixel : set to 0
blendStencil(getStencilAlpha(width-1,j), dst_data, 0, 0, 0);
dst_data += components;
south_data += buffer_size;
}
// Last line
for (S32 i = 0; i < width; i++)
{
blendStencil(getStencilAlpha(i,0), dst_data, 0, 0, 0);
dst_data += components;
}
}
void LLImageFilter::filterScreen(EScreenMode mode, const F32 wave_length, const F32 angle)
{
const S32 components = mImage->getComponents();
llassert( components >= 1 && components <= 4 );
S32 width = mImage->getWidth();
S32 height = mImage->getHeight();
F32 wave_length_pixels = wave_length * (F32)(height) / 2.0;
F32 sin = sinf(angle*DEG_TO_RAD);
F32 cos = cosf(angle*DEG_TO_RAD);
// Precompute the gamma table : gives us the gray level to use when cutting outside the screen (prevents strong aliasing on the screen)
U8 gamma[256];
for (S32 i = 0; i < 256; i++)
{
F32 gamma_i = llclampf((float)(powf((float)(i)/255.0,1.0/4.0)));
gamma[i] = (U8)(255.0 * gamma_i);
}
U8* dst_data = mImage->getData();
for (S32 j = 0; j < height; j++)
{
for (S32 i = 0; i < width; i++)
{
// Compute screen value
F32 value = 0.0;
F32 di = 0.0;
F32 dj = 0.0;
switch (mode)
{
case SCREEN_MODE_2DSINE:
di = cos*i + sin*j;
dj = -sin*i + cos*j;
value = (sinf(2*F_PI*di/wave_length_pixels)*sinf(2*F_PI*dj/wave_length_pixels)+1.0)*255.0/2.0;
break;
case SCREEN_MODE_LINE:
dj = sin*i - cos*j;
value = (sinf(2*F_PI*dj/wave_length_pixels)+1.0)*255.0/2.0;
break;
}
U8 dst_value = (dst_data[VRED] >= (U8)(value) ? gamma[dst_data[VRED] - (U8)(value)] : 0);
// Blend result
blendStencil(getStencilAlpha(i,j), dst_data, dst_value, dst_value, dst_value);
dst_data += components;
}
}
}
//============================================================================
// Procedural Stencils
//============================================================================
void LLImageFilter::setStencil(EStencilShape shape, EStencilBlendMode mode, F32 min, F32 max, F32* params)
{
mStencilShape = shape;
mStencilBlendMode = mode;
mStencilMin = llmin(llmax(min, -1.0f), 1.0f);
mStencilMax = llmin(llmax(max, -1.0f), 1.0f);
// Each shape will interpret the 4 params differenly.
// We compute each systematically, though, clearly, values are meaningless when the shape doesn't correspond to the parameters
mStencilCenterX = (S32)(mImage->getWidth() + params[0] * (F32)(mImage->getHeight()))/2;
mStencilCenterY = (S32)(mImage->getHeight() + params[1] * (F32)(mImage->getHeight()))/2;
mStencilWidth = (S32)(params[2] * (F32)(mImage->getHeight()))/2;
mStencilGamma = (params[3] <= 0.0 ? 1.0 : params[3]);
mStencilWavelength = (params[0] <= 0.0 ? 10.0 : params[0] * (F32)(mImage->getHeight()) / 2.0);
mStencilSine = sinf(params[1]*DEG_TO_RAD);
mStencilCosine = cosf(params[1]*DEG_TO_RAD);
mStencilStartX = ((F32)(mImage->getWidth()) + params[0] * (F32)(mImage->getHeight()))/2.0;
mStencilStartY = ((F32)(mImage->getHeight()) + params[1] * (F32)(mImage->getHeight()))/2.0;
F32 end_x = ((F32)(mImage->getWidth()) + params[2] * (F32)(mImage->getHeight()))/2.0;
F32 end_y = ((F32)(mImage->getHeight()) + params[3] * (F32)(mImage->getHeight()))/2.0;
mStencilGradX = end_x - mStencilStartX;
mStencilGradY = end_y - mStencilStartY;
mStencilGradN = mStencilGradX*mStencilGradX + mStencilGradY*mStencilGradY;
}
F32 LLImageFilter::getStencilAlpha(S32 i, S32 j)
{
F32 alpha = 1.0; // That init actually takes care of the STENCIL_SHAPE_UNIFORM case...
if (mStencilShape == STENCIL_SHAPE_VIGNETTE)
{
// alpha is a modified gaussian value, with a center and fading in a circular pattern toward the edges
// The gamma parameter controls the intensity of the drop down from alpha 1.0 (center) to 0.0
F32 d_center_square = (i - mStencilCenterX)*(i - mStencilCenterX) + (j - mStencilCenterY)*(j - mStencilCenterY);
alpha = powf(F_E, -(powf((d_center_square/(mStencilWidth*mStencilWidth)),mStencilGamma)/2.0f));
}
else if (mStencilShape == STENCIL_SHAPE_SCAN_LINES)
{
// alpha varies according to a squared sine function.
F32 d = mStencilSine*i - mStencilCosine*j;
alpha = (sinf(2*F_PI*d/mStencilWavelength) > 0.0 ? 1.0 : 0.0);
}
else if (mStencilShape == STENCIL_SHAPE_GRADIENT)
{
alpha = (((F32)(i) - mStencilStartX)*mStencilGradX + ((F32)(j) - mStencilStartY)*mStencilGradY) / mStencilGradN;
alpha = llclampf(alpha);
}
// We rescale alpha between min and max
return (mStencilMin + alpha * (mStencilMax - mStencilMin));
}
//============================================================================
// Histograms
//============================================================================
U32* LLImageFilter::getBrightnessHistogram()
{
if (!mHistoBrightness)
{
computeHistograms();
}
return mHistoBrightness;
}
void LLImageFilter::computeHistograms()
{
const S32 components = mImage->getComponents();
llassert( components >= 1 && components <= 4 );
// Allocate memory for the histograms
if (!mHistoRed)
{
mHistoRed = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
}
if (!mHistoGreen)
{
mHistoGreen = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
}
if (!mHistoBlue)
{
mHistoBlue = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
}
if (!mHistoBrightness)
{
mHistoBrightness = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
}
// Initialize them
for (S32 i = 0; i < 256; i++)
{
mHistoRed[i] = 0;
mHistoGreen[i] = 0;
mHistoBlue[i] = 0;
mHistoBrightness[i] = 0;
}
// Compute them
S32 pixels = mImage->getWidth() * mImage->getHeight();
U8* dst_data = mImage->getData();
for (S32 i = 0; i < pixels; i++)
{
mHistoRed[dst_data[VRED]]++;
mHistoGreen[dst_data[VGREEN]]++;
mHistoBlue[dst_data[VBLUE]]++;
// Note: this is a very simple shorthand for brightness but it's OK for our use
S32 brightness = ((S32)(dst_data[VRED]) + (S32)(dst_data[VGREEN]) + (S32)(dst_data[VBLUE])) / 3;
mHistoBrightness[brightness]++;
// next pixel...
dst_data += components;
}
}
//============================================================================
// Secondary Filters
//============================================================================
void LLImageFilter::filterGrayScale()
{
LLMatrix3 gray_scale;
LLVector3 luminosity(0.2125, 0.7154, 0.0721);
gray_scale.setRows(luminosity, luminosity, luminosity);
gray_scale.transpose();
colorTransform(gray_scale);
}
void LLImageFilter::filterSepia()
{
LLMatrix3 sepia;
sepia.setRows(LLVector3(0.3588, 0.7044, 0.1368),
LLVector3(0.2990, 0.5870, 0.1140),
LLVector3(0.2392, 0.4696, 0.0912));
sepia.transpose();
colorTransform(sepia);
}
void LLImageFilter::filterSaturate(F32 saturation)
{
// Matrix to Lij
LLMatrix3 r_a;
LLMatrix3 r_b;
// 45 degre rotation around z
r_a.setRows(LLVector3( OO_SQRT2, OO_SQRT2, 0.0),
LLVector3(-OO_SQRT2, OO_SQRT2, 0.0),
LLVector3( 0.0, 0.0, 1.0));
// 54.73 degre rotation around y
float oo_sqrt3 = 1.0f / F_SQRT3;
float sin_54 = F_SQRT2 * oo_sqrt3;
r_b.setRows(LLVector3(oo_sqrt3, 0.0, -sin_54),
LLVector3(0.0, 1.0, 0.0),
LLVector3(sin_54, 0.0, oo_sqrt3));
// Coordinate conversion
LLMatrix3 Lij = r_b * r_a;
LLMatrix3 Lij_inv = Lij;
Lij_inv.transpose();
// Local saturation transform
LLMatrix3 s;
s.setRows(LLVector3(saturation, 0.0, 0.0),
LLVector3(0.0, saturation, 0.0),
LLVector3(0.0, 0.0, 1.0));
// Global saturation transform
LLMatrix3 transfo = Lij_inv * s * Lij;
colorTransform(transfo);
}
void LLImageFilter::filterRotate(F32 angle)
{
// Matrix to Lij
LLMatrix3 r_a;
LLMatrix3 r_b;
// 45 degre rotation around z
r_a.setRows(LLVector3( OO_SQRT2, OO_SQRT2, 0.0),
LLVector3(-OO_SQRT2, OO_SQRT2, 0.0),
LLVector3( 0.0, 0.0, 1.0));
// 54.73 degre rotation around y
float oo_sqrt3 = 1.0f / F_SQRT3;
float sin_54 = F_SQRT2 * oo_sqrt3;
r_b.setRows(LLVector3(oo_sqrt3, 0.0, -sin_54),
LLVector3(0.0, 1.0, 0.0),
LLVector3(sin_54, 0.0, oo_sqrt3));
// Coordinate conversion
LLMatrix3 Lij = r_b * r_a;
LLMatrix3 Lij_inv = Lij;
Lij_inv.transpose();
// Local color rotation transform
LLMatrix3 r;
angle *= DEG_TO_RAD;
r.setRows(LLVector3( cosf(angle), sinf(angle), 0.0),
LLVector3(-sinf(angle), cosf(angle), 0.0),
LLVector3( 0.0, 0.0, 1.0));
// Global color rotation transform
LLMatrix3 transfo = Lij_inv * r * Lij;
colorTransform(transfo);
}
void LLImageFilter::filterGamma(F32 gamma, const LLColor3& alpha)
{
U8 gamma_red_lut[256];
U8 gamma_green_lut[256];
U8 gamma_blue_lut[256];
for (S32 i = 0; i < 256; i++)
{
F32 gamma_i = llclampf((float)(powf((float)(i)/255.0,1.0/gamma)));
// Blend in with alpha values
gamma_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * 255.0 * gamma_i);
gamma_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * 255.0 * gamma_i);
gamma_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * 255.0 * gamma_i);
}
colorCorrect(gamma_red_lut,gamma_green_lut,gamma_blue_lut);
}
void LLImageFilter::filterLinearize(F32 tail, const LLColor3& alpha)
{
// Get the histogram
U32* histo = getBrightnessHistogram();
// Compute cumulated histogram
U32 cumulated_histo[256];
cumulated_histo[0] = histo[0];
for (S32 i = 1; i < 256; i++)
{
cumulated_histo[i] = cumulated_histo[i-1] + histo[i];
}
// Compute min and max counts minus tail
tail = llclampf(tail);
S32 total = cumulated_histo[255];
S32 min_c = (S32)((F32)(total) * tail);
S32 max_c = (S32)((F32)(total) * (1.0 - tail));
// Find min and max values
S32 min_v = 0;
while (cumulated_histo[min_v] < min_c)
{
min_v++;
}
S32 max_v = 255;
while (cumulated_histo[max_v] > max_c)
{
max_v--;
}
// Compute linear lookup table
U8 linear_red_lut[256];
U8 linear_green_lut[256];
U8 linear_blue_lut[256];
if (max_v == min_v)
{
// Degenerated binary split case
for (S32 i = 0; i < 256; i++)
{
U8 value_i = (i < min_v ? 0 : 255);
// Blend in with alpha values
linear_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
linear_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i);
linear_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i);
}
}
else
{
// Linearize between min and max
F32 slope = 255.0 / (F32)(max_v - min_v);
F32 translate = -min_v * slope;
for (S32 i = 0; i < 256; i++)
{
U8 value_i = (U8)(llclampb((S32)(slope*i + translate)));
// Blend in with alpha values
linear_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
linear_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i);
linear_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i);
}
}
// Apply lookup table
colorCorrect(linear_red_lut,linear_green_lut,linear_blue_lut);
}
void LLImageFilter::filterEqualize(S32 nb_classes, const LLColor3& alpha)
{
// Regularize the parameter: must be between 2 and 255
nb_classes = llmax(nb_classes,2);
nb_classes = llclampb(nb_classes);
// Get the histogram
U32* histo = getBrightnessHistogram();
// Compute cumulated histogram
U32 cumulated_histo[256];
cumulated_histo[0] = histo[0];
for (S32 i = 1; i < 256; i++)
{
cumulated_histo[i] = cumulated_histo[i-1] + histo[i];
}
// Compute deltas
S32 total = cumulated_histo[255];
S32 delta_count = total / nb_classes;
S32 current_count = delta_count;
S32 delta_value = 256 / (nb_classes - 1);
S32 current_value = 0;
// Compute equalized lookup table
U8 equalize_red_lut[256];
U8 equalize_green_lut[256];
U8 equalize_blue_lut[256];
for (S32 i = 0; i < 256; i++)
{
// Blend in current_value with alpha values
equalize_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * current_value);
equalize_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * current_value);
equalize_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * current_value);
if (cumulated_histo[i] >= current_count)
{
current_count += delta_count;
current_value += delta_value;
current_value = llclampb(current_value);
}
}
// Apply lookup table
colorCorrect(equalize_red_lut,equalize_green_lut,equalize_blue_lut);
}
void LLImageFilter::filterColorize(const LLColor3& color, const LLColor3& alpha)
{
U8 red_lut[256];
U8 green_lut[256];
U8 blue_lut[256];
F32 red_composite = 255.0 * alpha.mV[0] * color.mV[0];
F32 green_composite = 255.0 * alpha.mV[1] * color.mV[1];
F32 blue_composite = 255.0 * alpha.mV[2] * color.mV[2];
for (S32 i = 0; i < 256; i++)
{
red_lut[i] = (U8)(llclampb((S32)((1.0 - alpha.mV[0]) * (F32)(i) + red_composite)));
green_lut[i] = (U8)(llclampb((S32)((1.0 - alpha.mV[1]) * (F32)(i) + green_composite)));
blue_lut[i] = (U8)(llclampb((S32)((1.0 - alpha.mV[2]) * (F32)(i) + blue_composite)));
}
colorCorrect(red_lut,green_lut,blue_lut);
}
void LLImageFilter::filterContrast(F32 slope, const LLColor3& alpha)
{
U8 contrast_red_lut[256];
U8 contrast_green_lut[256];
U8 contrast_blue_lut[256];
F32 translate = 128.0 * (1.0 - slope);
for (S32 i = 0; i < 256; i++)
{
U8 value_i = (U8)(llclampb((S32)(slope*i + translate)));
// Blend in with alpha values
contrast_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
contrast_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i);
contrast_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i);
}
colorCorrect(contrast_red_lut,contrast_green_lut,contrast_blue_lut);
}
void LLImageFilter::filterBrightness(F32 add, const LLColor3& alpha)
{
U8 brightness_red_lut[256];
U8 brightness_green_lut[256];
U8 brightness_blue_lut[256];
S32 add_value = (S32)(add * 255.0);
for (S32 i = 0; i < 256; i++)
{
U8 value_i = (U8)(llclampb(i + add_value));
// Blend in with alpha values
brightness_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
brightness_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i);
brightness_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i);
}
colorCorrect(brightness_red_lut,brightness_green_lut,brightness_blue_lut);
}
//============================================================================

137
indra/llimage/llimagefilter.h Executable file
View File

@ -0,0 +1,137 @@
/**
* @file llimagefilter.h
* @brief Simple Image Filtering. See https://wiki.lindenlab.com/wiki/SL_Viewer_Image_Filters for complete documentation.
*
* $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2014, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLIMAGEFILTER_H
#define LL_LLIMAGEFILTER_H
#include "llsd.h"
#include "llimage.h"
class LLImageRaw;
class LLColor4U;
class LLColor3;
class LLMatrix3;
typedef enum e_stencil_blend_mode
{
STENCIL_BLEND_MODE_BLEND = 0,
STENCIL_BLEND_MODE_ADD = 1,
STENCIL_BLEND_MODE_ABACK = 2,
STENCIL_BLEND_MODE_FADE = 3
} EStencilBlendMode;
typedef enum e_stencil_shape
{
STENCIL_SHAPE_UNIFORM = 0,
STENCIL_SHAPE_GRADIENT = 1,
STENCIL_SHAPE_VIGNETTE = 2,
STENCIL_SHAPE_SCAN_LINES = 3
} EStencilShape;
typedef enum e_screen_mode
{
SCREEN_MODE_2DSINE = 0,
SCREEN_MODE_LINE = 1
} EScreenMode;
//============================================================================
// LLImageFilter
//============================================================================
class LLImageFilter
{
public:
LLImageFilter(const std::string& file_path);
~LLImageFilter();
void executeFilter(LLPointer<LLImageRaw> raw_image);
private:
// Filter Operations : Transforms
void filterGrayScale(); // Convert to grayscale
void filterSepia(); // Convert to sepia
void filterSaturate(F32 saturation); // < 1.0 desaturates, > 1.0 saturates
void filterRotate(F32 angle); // Rotates hue according to angle, angle in degrees
// Filter Operations : Color Corrections
// When specified, the LLColor3 alpha parameter indicates the intensity of the effect for each color channel
// acting in effect as an alpha blending factor different for each channel. For instance (1.0,0.0,0.0) will apply
// the effect only to the Red channel. Intermediate values blends the effect with the source color.
void filterGamma(F32 gamma, const LLColor3& alpha); // Apply gamma to each channel
void filterLinearize(F32 tail, const LLColor3& alpha); // Use histogram to linearize constrast between min and max values minus tail
void filterEqualize(S32 nb_classes, const LLColor3& alpha); // Use histogram to equalize constrast between nb_classes throughout the image
void filterColorize(const LLColor3& color, const LLColor3& alpha); // Colorize with color and alpha per channel
void filterContrast(F32 slope, const LLColor3& alpha); // Change contrast according to slope: > 1.0 more contrast, < 1.0 less contrast
void filterBrightness(F32 add, const LLColor3& alpha); // Change brightness according to add: > 0 brighter, < 0 darker
// Filter Primitives
void colorTransform(const LLMatrix3 &transform);
void colorCorrect(const U8* lut_red, const U8* lut_green, const U8* lut_blue);
void filterScreen(EScreenMode mode, const F32 wave_length, const F32 angle);
void blendStencil(F32 alpha, U8* pixel, U8 red, U8 green, U8 blue);
void convolve(const LLMatrix3 &kernel, bool normalize, bool abs_value);
// Procedural Stencils
void setStencil(EStencilShape shape, EStencilBlendMode mode, F32 min, F32 max, F32* params);
F32 getStencilAlpha(S32 i, S32 j);
// Histograms
U32* getBrightnessHistogram();
void computeHistograms();
LLSD mFilterData;
LLPointer<LLImageRaw> mImage;
// Histograms (if we ever happen to need them)
U32 *mHistoRed;
U32 *mHistoGreen;
U32 *mHistoBlue;
U32 *mHistoBrightness;
// Current Stencil Settings
EStencilBlendMode mStencilBlendMode;
EStencilShape mStencilShape;
F32 mStencilMin;
F32 mStencilMax;
S32 mStencilCenterX;
S32 mStencilCenterY;
S32 mStencilWidth;
F32 mStencilGamma;
F32 mStencilWavelength;
F32 mStencilSine;
F32 mStencilCosine;
F32 mStencilStartX;
F32 mStencilStartY;
F32 mStencilGradX;
F32 mStencilGradY;
F32 mStencilGradN;
};
#endif

View File

@ -310,6 +310,7 @@ set(viewer_SOURCE_FILES
llfilteredwearablelist.cpp
llfirstuse.cpp
llflexibleobject.cpp
llflickrconnect.cpp
llfloaterabout.cpp
llfloaterbvhpreview.cpp
llfloaterauction.cpp
@ -318,6 +319,7 @@ set(viewer_SOURCE_FILES
llfloateravatarpicker.cpp
llfloateravatartextures.cpp
llfloaterbeacons.cpp
llfloaterbigpreview.cpp
llfloaterbuildoptions.cpp
llfloaterbulkpermission.cpp
llfloaterbump.cpp
@ -339,6 +341,8 @@ set(viewer_SOURCE_FILES
llfloatereditwater.cpp
llfloaterenvironmentsettings.cpp
llfloaterevent.cpp
llfloaterfacebook.cpp
llfloaterflickr.cpp
llfloaterfonttest.cpp
llfloatergesture.cpp
llfloatergodtools.cpp
@ -391,7 +395,6 @@ set(viewer_SOURCE_FILES
llfloatersettingsdebug.cpp
llfloatersidepanelcontainer.cpp
llfloatersnapshot.cpp
llfloatersocial.cpp
llfloatersounddevices.cpp
llfloaterspellchecksettings.cpp
llfloatertelehub.cpp
@ -403,6 +406,7 @@ set(viewer_SOURCE_FILES
llfloatertos.cpp
llfloatertoybox.cpp
llfloatertranslationsettings.cpp
llfloatertwitter.cpp
llfloateruipreview.cpp
llfloaterurlentry.cpp
llfloatervoiceeffect.cpp
@ -440,6 +444,7 @@ set(viewer_SOURCE_FILES
llhudrender.cpp
llhudtext.cpp
llhudview.cpp
llimagefiltersmanager.cpp
llimhandler.cpp
llimview.cpp
llinspect.cpp
@ -683,6 +688,7 @@ set(viewer_SOURCE_FILES
lltransientdockablefloater.cpp
lltransientfloatermgr.cpp
lltranslate.cpp
lltwitterconnect.cpp
lluilistener.cpp
lluploaddialog.cpp
lluploadfloaterobservers.cpp
@ -1028,6 +1034,7 @@ set(viewer_HEADER_FILES
llfilteredwearablelist.h
llfirstuse.h
llflexibleobject.h
llflickrconnect.h
llfloaterabout.h
llfloaterbvhpreview.h
llfloaterauction.h
@ -1036,6 +1043,7 @@ set(viewer_HEADER_FILES
llfloateravatarpicker.h
llfloateravatartextures.h
llfloaterbeacons.h
llfloaterbigpreview.h
llfloaterbuildoptions.h
llfloaterbulkpermission.h
llfloaterbump.h
@ -1057,6 +1065,8 @@ set(viewer_HEADER_FILES
llfloatereditwater.h
llfloaterenvironmentsettings.h
llfloaterevent.h
llfloaterfacebook.h
llfloaterflickr.h
llfloaterfonttest.h
llfloatergesture.h
llfloatergodtools.h
@ -1112,7 +1122,6 @@ set(viewer_HEADER_FILES
llfloatersettingsdebug.h
llfloatersidepanelcontainer.h
llfloatersnapshot.h
llfloatersocial.h
llfloatersounddevices.h
llfloaterspellchecksettings.h
llfloatertelehub.h
@ -1124,6 +1133,7 @@ set(viewer_HEADER_FILES
llfloatertos.h
llfloatertoybox.h
llfloatertranslationsettings.h
llfloatertwitter.h
llfloateruipreview.h
llfloaterurlentry.h
llfloatervoiceeffect.h
@ -1160,6 +1170,7 @@ set(viewer_HEADER_FILES
llhudrender.h
llhudtext.h
llhudview.h
llimagefiltersmanager.h
llimview.h
llinspect.h
llinspectavatar.h
@ -1391,6 +1402,7 @@ set(viewer_HEADER_FILES
lltransientdockablefloater.h
lltransientfloatermgr.h
lltranslate.h
lltwitterconnect.h
lluiconstants.h
lluilistener.h
lluploaddialog.h

View File

@ -224,15 +224,35 @@
is_running_function="Floater.IsOpen"
is_running_parameters="snapshot"
/>
<command name="social"
<command name="facebook"
available_in_toybox="true"
icon="Command_Social_Icon"
label_ref="Command_Social_Label"
tooltip_ref="Command_Social_Tooltip"
icon="Command_Facebook_Icon"
label_ref="Command_Facebook_Label"
tooltip_ref="Command_Facebook_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
execute_parameters="social"
execute_parameters="facebook"
is_running_function="Floater.IsOpen"
is_running_parameters="social"
is_running_parameters="facebook"
/>
<command name="flickr"
available_in_toybox="true"
icon="Command_Flickr_Icon"
label_ref="Command_Flickr_Label"
tooltip_ref="Command_Flickr_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
execute_parameters="flickr"
is_running_function="Floater.IsOpen"
is_running_parameters="flickr"
/>
<command name="twitter"
available_in_toybox="true"
icon="Command_Twitter_Icon"
label_ref="Command_Twitter_Label"
tooltip_ref="Command_Twitter_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
execute_parameters="twitter"
is_running_function="Floater.IsOpen"
is_running_parameters="twitter"
/>
<command name="speak"
available_in_toybox="true"

View File

@ -0,0 +1,11 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.01</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,21 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.01</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>contrast</string>
<real>0.8</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>grayscale</string>
</array>
</array>
</llsd>

View File

@ -0,0 +1,47 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.1</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>contrast</string>
<real>0.8</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.3</real>
<real>0.0</real>
<real>0.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>blend</string>
<real>0.0</real>
<real>1.0</real>
<real>0.0</real>
<real>0.0</real>
<real>1.0</real>
<real>10.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.1</real>
<real>0.1</real>
<real>0.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,8 @@
<llsd>
<array>
<array>
<string>saturate</string>
<real>3.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,131 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.01</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>stencil</string>
<string>gradient</string>
<string>add</string>
<real>0.5</real>
<real>0.0</real>
<real>-1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>-1.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.1</real>
<real>0.1</real>
<real>0.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>add</string>
<real>0.0</real>
<real>0.5</real>
<real>-1.0</real>
<real>1.0</real>
<real>1.5</real>
<real>5.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.6</real>
<real>0.0</real>
<real>0.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>add</string>
<real>0.0</real>
<real>0.5</real>
<real>-1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>5.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.6</real>
<real>0.6</real>
<real>0.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>add</string>
<real>0.0</real>
<real>0.5</real>
<real>0.5</real>
<real>-0.5</real>
<real>0.10</real>
<real>20.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.7</real>
<real>0.0</real>
<real>0.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>add</string>
<real>0.0</real>
<real>0.5</real>
<real>0.6</real>
<real>-0.6</real>
<real>0.05</real>
<real>20.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.7</real>
<real>0.0</real>
<real>0.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>add</string>
<real>0.0</real>
<real>0.5</real>
<real>0.4</real>
<real>-0.4</real>
<real>0.025</real>
<real>20.0</real>
</array>
<array>
<string>colorize</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>0.7</real>
<real>0.0</real>
<real>0.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,118 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.02</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>contrast</string>
<real>1.02</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>saturate</string>
<real>1.2</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>blend</string>
<real>0.0</real>
<real>0.25</real>
<real>0.0</real>
<real>0.0</real>
<real>0.25</real>
<real>2.0</real>
</array>
<array>
<string>sharpen</string>
</array>
<array>
<string>stencil</string>
<string>gradient</string>
<string>blend</string>
<real>1.0</real>
<real>0.0</real>
<real>0.0</real>
<real>-1.0</real>
<real>0.0</real>
<real>-0.25</real>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>stencil</string>
<string>gradient</string>
<string>blend</string>
<real>1.0</real>
<real>0.0</real>
<real>0.0</real>
<real>1.0</real>
<real>0.0</real>
<real>0.25</real>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
</array>
</llsd>

View File

@ -0,0 +1,20 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.1</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>grayscale</string>
</array>
<array>
<string>screen</string>
<string>2Dsine</string>
<real>0.02</real>
<real>0.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,32 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.01</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>contrast</string>
<real>0.8</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>fade</string>
<real>0.5</real>
<real>1.0</real>
<real>0.0</real>
<real>0.0</real>
<real>1.0</real>
<real>4.0</real>
</array>
<array>
<string>sepia</string>
</array>
</array>
</llsd>

View File

@ -0,0 +1,47 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>add</string>
<real>0.0</real>
<real>0.4</real>
<real>0.0</real>
<real>0.0</real>
<real>1.0</real>
<real>2.0</real>
</array>
<array>
<string>contrast</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>add</string>
<real>-0.8</real>
<real>0.0</real>
<real>0.0</real>
<real>0.0</real>
<real>1.0</real>
<real>2.0</real>
</array>
<array>
<string>contrast</string>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
</array>
</llsd>

View File

@ -0,0 +1,46 @@
<llsd>
<array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>fade</string>
<real>0.0</real>
<real>1.0</real>
<real>0.0</real>
<real>0.0</real>
<real>1.2</real>
<real>3.0</real>
</array>
<array>
<string>linearize</string>
<real>0.05</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>grayscale</string>
</array>
<array>
<string>contrast</string>
<real>1.1</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>stencil</string>
<string>vignette</string>
<string>blend</string>
<real>1.0</real>
<real>0.0</real>
<real>0.0</real>
<real>0.0</real>
<real>0.5</real>
<real>2.0</real>
</array>
<array>
<string>blur</string>
</array>
</array>
</llsd>

View File

@ -0,0 +1,44 @@
<llsd>
<array>
<array>
<string>linearize</string>
<real>0.0</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>darken</string>
<real>0.15</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>stencil</string>
<string>uniform</string>
<string>add</string>
<real>0.0</real>
<real>0.5</real>
</array>
<array>
<string>screen</string>
<string>line</string>
<real>0.02</real>
<real>0.0</real>
</array>
<array>
<string>gamma</string>
<real>0.25</real>
<real>1.0</real>
<real>1.0</real>
<real>1.0</real>
</array>
<array>
<string>blur</string>
</array>
<array>
<string>blur</string>
</array>
</array>
</llsd>

View File

@ -14313,6 +14313,17 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Value</key>
<integer>0</integer>
</map>
<key>SnapshotFiltersEnabled</key>
<map>
<key>Comment</key>
<string>Enable filters in the Snapshot Advanced panel (experimental).</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>SnapshotFormat</key>
<map>
<key>Comment</key>
@ -16599,7 +16610,7 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>SocialPhotoResolution</key>
<map>
<key>Comment</key>
<string>Default resolution when sharing photo using the social floater</string>
<string>Default resolution when sharing photo using the social floaters</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>

View File

@ -28,6 +28,8 @@
#include "llviewerprecompiledheaders.h"
#include "llfacebookconnect.h"
#include "llflickrconnect.h"
#include "lltwitterconnect.h"
#include "llagent.h"
#include "llcallingcard.h" // for LLAvatarTracker
@ -58,7 +60,7 @@ void log_facebook_connect_error(const std::string& request, U32 status, const st
}
}
void toast_user_for_success()
void toast_user_for_facebook_success()
{
LLSD args;
args["MESSAGE"] = LLTrans::getString("facebook_post_success");
@ -74,23 +76,46 @@ public:
bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web)
{
if (tokens.size() > 0)
if (tokens.size() >= 1)
{
if (tokens[0].asString() == "connect")
{
// this command probably came from the fbc_web browser, so close it
LLFloater* fbc_web = LLFloaterReg::getInstance("fbc_web");
if (fbc_web)
if (tokens.size() >= 2 && tokens[1].asString() == "flickr")
{
fbc_web->closeFloater();
}
// this command probably came from the flickr_web browser, so close it
LLFloaterReg::hideInstance("flickr_web");
// connect to facebook
if (query_map.has("code"))
{
LLFacebookConnect::instance().connectToFacebook(query_map["code"], query_map.get("state"));
// connect to flickr
if (query_map.has("oauth_token"))
{
LLFlickrConnect::instance().connectToFlickr(query_map["oauth_token"], query_map.get("oauth_verifier"));
}
return true;
}
else if (tokens.size() >= 2 && tokens[1].asString() == "twitter")
{
// this command probably came from the twitter_web browser, so close it
LLFloaterReg::hideInstance("twitter_web");
// connect to twitter
if (query_map.has("oauth_token"))
{
LLTwitterConnect::instance().connectToTwitter(query_map["oauth_token"], query_map.get("oauth_verifier"));
}
return true;
}
else //if (tokens.size() >= 2 && tokens[1].asString() == "facebook")
{
// this command probably came from the fbc_web browser, so close it
LLFloaterReg::hideInstance("fbc_web");
// connect to facebook
if (query_map.has("code"))
{
LLFacebookConnect::instance().connectToFacebook(query_map["code"], query_map.get("state"));
}
return true;
}
return true;
}
}
return false;
@ -156,7 +181,7 @@ public:
/* virtual */ void httpSuccess()
{
toast_user_for_success();
toast_user_for_facebook_success();
LL_DEBUGS("FacebookConnect") << "Post successful. " << dumpResponse() << LL_ENDL;
LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_POSTED);
}
@ -302,9 +327,16 @@ public:
{
if ( HTTP_FOUND == getStatus() )
{
LL_INFOS() << "Facebook: Info received" << LL_ENDL;
LL_DEBUGS("FacebookConnect") << "Getting Facebook info successful. info: " << getContent() << LL_ENDL;
LLFacebookConnect::instance().storeInfo(getContent());
const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION);
if (location.empty())
{
LL_WARNS("FacebookConnect") << "Missing Location header " << dumpResponse()
<< "[headers:" << getResponseHeaders() << "]" << LL_ENDL;
}
else
{
LLFacebookConnect::instance().openFacebookWeb(location);
}
}
else
{
@ -371,10 +403,12 @@ void LLFacebookConnect::openFacebookWeb(std::string url)
{
// Open the URL in an internal browser window without navigation UI
LLFloaterWebContent::Params p;
p.url(url).show_chrome(true);
p.url(url).allow_address_entry(false);
p.url(url).allow_back_forward_navigation(false);
p.url(url).trusted_content(true);
p.url(url);
p.show_chrome(true);
p.allow_address_entry(false);
p.allow_back_forward_navigation(false);
p.trusted_content(true);
p.clean_browser(true);
LLFloater *floater = LLFloaterReg::showInstance("fbc_web", p);
//the internal web browser has a bug that prevents it from gaining focus unless a mouse event occurs first (it seems).
//So when showing the internal web browser, set focus to it's containing floater "fbc_web". When a mouse event
@ -391,7 +425,8 @@ std::string LLFacebookConnect::getFacebookConnectURL(const std::string& route, b
LLViewerRegion *regionp = gAgent.getRegion();
if (regionp)
{
url = regionp->getCapability("FacebookConnect");
//url = "http://pdp15.lindenlab.com/fbc/agent/" + gAgentID.asString(); // TEMPORARY FOR TESTING - CHO
url = regionp->getCapability("FacebookConnect");
url += route;
if (include_read_from_master && mReadFromMaster)
@ -406,9 +441,13 @@ void LLFacebookConnect::connectToFacebook(const std::string& auth_code, const st
{
LLSD body;
if (!auth_code.empty())
{
body["code"] = auth_code;
}
if (!auth_state.empty())
{
body["state"] = auth_state;
}
LLHTTPClient::put(getFacebookConnectURL("/connection"), body, new LLFacebookConnectResponder());
}
@ -452,15 +491,25 @@ void LLFacebookConnect::postCheckin(const std::string& location, const std::stri
{
LLSD body;
if (!location.empty())
{
body["location"] = location;
}
if (!name.empty())
{
body["name"] = name;
}
if (!description.empty())
{
body["description"] = description;
}
if (!image.empty())
{
body["image"] = image;
}
if (!message.empty())
{
body["message"] = message;
}
// Note: we can use that route for different publish action. We should be able to use the same responder.
LLHTTPClient::post(getFacebookConnectURL("/share/checkin", true), body, new LLFacebookShareResponder());
@ -507,7 +556,7 @@ void LLFacebookConnect::sharePhoto(LLPointer<LLImageFormatted> image, const std:
<< caption << "\r\n";
body << "--" << boundary << "\r\n"
<< "Content-Disposition: form-data; name=\"image\"; filename=\"snapshot." << imageFormat << "\"\r\n"
<< "Content-Disposition: form-data; name=\"image\"; filename=\"Untitled." << imageFormat << "\"\r\n"
<< "Content-Type: image/" << imageFormat << "\r\n\r\n";
// Insert the image data.
@ -599,12 +648,13 @@ void LLFacebookConnect::setConnectionState(LLFacebookConnect::EConnectionState c
if (mConnectionState != connection_state)
{
// set the connection state before notifying watchers
mConnectionState = connection_state;
LLSD state_info;
state_info["enum"] = connection_state;
sStateWatcher->post(state_info);
}
mConnectionState = connection_state;
}
void LLFacebookConnect::setConnected(bool connected)

View File

@ -89,7 +89,7 @@ private:
LLFacebookConnect();
~LLFacebookConnect() {};
std::string getFacebookConnectURL(const std::string& route = "", bool include_read_from_master = false);
EConnectionState mConnectionState;
BOOL mConnected;
LLSD mInfo;

View File

@ -171,7 +171,8 @@ BOOL LLFilePicker::setupFilter(ELoadFilter filter)
BOOL res = TRUE;
switch (filter)
{
case FFLOAD_ALL:
case FFLOAD_ALL:
case FFLOAD_EXE:
mOFN.lpstrFilter = L"All Files (*.*)\0*.*\0" \
SOUND_FILTER \
IMAGE_FILTER \
@ -650,6 +651,10 @@ std::vector<std::string>* LLFilePicker::navOpenFilterProc(ELoadFilter filter) //
allowedv->push_back("tpic");
allowedv->push_back("png");
break;
case FFLOAD_EXE:
allowedv->push_back("app");
allowedv->push_back("exe");
break;
case FFLOAD_WAV:
allowedv->push_back("wav");
break;
@ -881,7 +886,7 @@ BOOL LLFilePicker::getOpenFile(ELoadFilter filter, bool blocking)
mPickOptions &= ~F_FILE;
}
if(filter == FFLOAD_ALL) // allow application bundles etc. to be traversed; important for DEV-16869, but generally useful
if (filter == FFLOAD_ALL) // allow application bundles etc. to be traversed; important for DEV-16869, but generally useful
{
mPickOptions |= F_NAV_SUPPORT;
}

View File

@ -88,6 +88,7 @@ public:
FFLOAD_SCRIPT = 11,
FFLOAD_DICTIONARY = 12,
FFLOAD_DIRECTORY = 13, //To call from lldirpicker.
FFLOAD_EXE = 14, // Note: EXE will be treated as ALL on Windows and Linux but not on Darwin
// Firestorm additions
FFLOAD_IMPORT = 50

View File

@ -0,0 +1,509 @@
/**
* @file llflickrconnect.h
* @author Merov, Cho
* @brief Connection to Flickr Service
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "llflickrconnect.h"
#include "llagent.h"
#include "llcallingcard.h" // for LLAvatarTracker
#include "llcommandhandler.h"
#include "llhttpclient.h"
#include "llnotificationsutil.h"
#include "llurlaction.h"
#include "llimagepng.h"
#include "llimagejpeg.h"
#include "lltrans.h"
#include "llevents.h"
#include "llviewerregion.h"
#include "llfloaterwebcontent.h"
#include "llfloaterreg.h"
boost::scoped_ptr<LLEventPump> LLFlickrConnect::sStateWatcher(new LLEventStream("FlickrConnectState"));
boost::scoped_ptr<LLEventPump> LLFlickrConnect::sInfoWatcher(new LLEventStream("FlickrConnectInfo"));
boost::scoped_ptr<LLEventPump> LLFlickrConnect::sContentWatcher(new LLEventStream("FlickrConnectContent"));
// Local functions
void log_flickr_connect_error(const std::string& request, U32 status, const std::string& reason, const std::string& code, const std::string& description)
{
// Note: 302 (redirect) is *not* an error that warrants logging
if (status != 302)
{
LL_WARNS("FlickrConnect") << request << " request failed with a " << status << " " << reason << ". Reason: " << code << " (" << description << ")" << LL_ENDL;
}
}
void toast_user_for_flickr_success()
{
LLSD args;
args["MESSAGE"] = LLTrans::getString("flickr_post_success");
LLNotificationsUtil::add("FlickrConnect", args);
}
///////////////////////////////////////////////////////////////////////////////
//
class LLFlickrConnectResponder : public LLHTTPClient::Responder
{
LOG_CLASS(LLFlickrConnectResponder);
public:
LLFlickrConnectResponder()
{
LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_IN_PROGRESS);
}
/* virtual */ void httpSuccess()
{
LL_DEBUGS("FlickrConnect") << "Connect successful. " << dumpResponse() << LL_ENDL;
LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTED);
}
/* virtual */ void httpFailure()
{
if ( HTTP_FOUND == getStatus() )
{
const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION);
if (location.empty())
{
LL_WARNS("FlickrConnect") << "Missing Location header " << dumpResponse()
<< "[headers:" << getResponseHeaders() << "]" << LL_ENDL;
}
else
{
LLFlickrConnect::instance().openFlickrWeb(location);
}
}
else
{
LL_WARNS("FlickrConnect") << dumpResponse() << LL_ENDL;
LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_FAILED);
const LLSD& content = getContent();
log_flickr_connect_error("Connect", getStatus(), getReason(),
content.get("error_code"), content.get("error_description"));
}
}
};
///////////////////////////////////////////////////////////////////////////////
//
class LLFlickrShareResponder : public LLHTTPClient::Responder
{
LOG_CLASS(LLFlickrShareResponder);
public:
LLFlickrShareResponder()
{
LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_POSTING);
}
/* virtual */ void httpSuccess()
{
toast_user_for_flickr_success();
LL_DEBUGS("FlickrConnect") << "Post successful. " << dumpResponse() << LL_ENDL;
LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_POSTED);
}
/* virtual */ void httpFailure()
{
if ( HTTP_FOUND == getStatus() )
{
const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION);
if (location.empty())
{
LL_WARNS("FlickrConnect") << "Missing Location header " << dumpResponse()
<< "[headers:" << getResponseHeaders() << "]" << LL_ENDL;
}
else
{
LLFlickrConnect::instance().openFlickrWeb(location);
}
}
else if ( HTTP_NOT_FOUND == getStatus() )
{
LLFlickrConnect::instance().connectToFlickr();
}
else
{
LL_WARNS("FlickrConnect") << dumpResponse() << LL_ENDL;
LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_POST_FAILED);
const LLSD& content = getContent();
log_flickr_connect_error("Share", getStatus(), getReason(),
content.get("error_code"), content.get("error_description"));
}
}
};
///////////////////////////////////////////////////////////////////////////////
//
class LLFlickrDisconnectResponder : public LLHTTPClient::Responder
{
LOG_CLASS(LLFlickrDisconnectResponder);
public:
LLFlickrDisconnectResponder()
{
LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_DISCONNECTING);
}
void setUserDisconnected()
{
// Clear data
LLFlickrConnect::instance().clearInfo();
//Notify state change
LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_NOT_CONNECTED);
}
/* virtual */ void httpSuccess()
{
LL_DEBUGS("FlickrConnect") << "Disconnect successful. " << dumpResponse() << LL_ENDL;
setUserDisconnected();
}
/* virtual */ void httpFailure()
{
//User not found so already disconnected
if ( HTTP_NOT_FOUND == getStatus() )
{
LL_DEBUGS("FlickrConnect") << "Already disconnected. " << dumpResponse() << LL_ENDL;
setUserDisconnected();
}
else
{
LL_WARNS("FlickrConnect") << dumpResponse() << LL_ENDL;
LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_DISCONNECT_FAILED);
const LLSD& content = getContent();
log_flickr_connect_error("Disconnect", getStatus(), getReason(),
content.get("error_code"), content.get("error_description"));
}
}
};
///////////////////////////////////////////////////////////////////////////////
//
class LLFlickrConnectedResponder : public LLHTTPClient::Responder
{
LOG_CLASS(LLFlickrConnectedResponder);
public:
LLFlickrConnectedResponder(bool auto_connect) : mAutoConnect(auto_connect)
{
LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_IN_PROGRESS);
}
/* virtual */ void httpSuccess()
{
LL_DEBUGS("FlickrConnect") << "Connect successful. " << dumpResponse() << LL_ENDL;
LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTED);
}
/* virtual */ void httpFailure()
{
// show the facebook login page if not connected yet
if ( HTTP_NOT_FOUND == getStatus() )
{
LL_DEBUGS("FlickrConnect") << "Not connected. " << dumpResponse() << LL_ENDL;
if (mAutoConnect)
{
LLFlickrConnect::instance().connectToFlickr();
}
else
{
LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_NOT_CONNECTED);
}
}
else
{
LL_WARNS("FlickrConnect") << dumpResponse() << LL_ENDL;
LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_FAILED);
const LLSD& content = getContent();
log_flickr_connect_error("Connected", getStatus(), getReason(),
content.get("error_code"), content.get("error_description"));
}
}
private:
bool mAutoConnect;
};
///////////////////////////////////////////////////////////////////////////////
//
class LLFlickrInfoResponder : public LLHTTPClient::Responder
{
LOG_CLASS(LLFlickrInfoResponder);
public:
/* virtual */ void httpSuccess()
{
LL_INFOS("FlickrConnect") << "Flickr: Info received" << LL_ENDL;
LL_DEBUGS("FlickrConnect") << "Getting Flickr info successful. " << dumpResponse() << LL_ENDL;
LLFlickrConnect::instance().storeInfo(getContent());
}
/* virtual */ void httpFailure()
{
if ( HTTP_FOUND == getStatus() )
{
const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION);
if (location.empty())
{
LL_WARNS("FlickrConnect") << "Missing Location header " << dumpResponse()
<< "[headers:" << getResponseHeaders() << "]" << LL_ENDL;
}
else
{
LLFlickrConnect::instance().openFlickrWeb(location);
}
}
else
{
LL_WARNS("FlickrConnect") << dumpResponse() << LL_ENDL;
const LLSD& content = getContent();
log_flickr_connect_error("Info", getStatus(), getReason(),
content.get("error_code"), content.get("error_description"));
}
}
};
///////////////////////////////////////////////////////////////////////////////
//
LLFlickrConnect::LLFlickrConnect()
: mConnectionState(FLICKR_NOT_CONNECTED),
mConnected(false),
mInfo(),
mRefreshInfo(false),
mReadFromMaster(false)
{
}
void LLFlickrConnect::openFlickrWeb(std::string url)
{
// Open the URL in an internal browser window without navigation UI
LLFloaterWebContent::Params p;
p.url(url);
p.show_chrome(true);
p.allow_address_entry(false);
p.allow_back_forward_navigation(false);
p.trusted_content(true);
p.clean_browser(true);
LLFloater *floater = LLFloaterReg::showInstance("flickr_web", p);
//the internal web browser has a bug that prevents it from gaining focus unless a mouse event occurs first (it seems).
//So when showing the internal web browser, set focus to it's containing floater "flickr_web". When a mouse event
//occurs on the "webbrowser" panel part of the floater, a mouse cursor will properly show and the "webbrowser" will gain focus.
//flickr_web floater contains the "webbrowser" panel. JIRA: ACME-744
gFocusMgr.setKeyboardFocus( floater );
//LLUrlAction::openURLExternal(url);
}
std::string LLFlickrConnect::getFlickrConnectURL(const std::string& route, bool include_read_from_master)
{
std::string url("");
LLViewerRegion *regionp = gAgent.getRegion();
if (regionp)
{
//url = "http://pdp15.lindenlab.com/flickr/agent/" + gAgentID.asString(); // TEMPORARY FOR TESTING - CHO
url = regionp->getCapability("FlickrConnect");
url += route;
if (include_read_from_master && mReadFromMaster)
{
url += "?read_from_master=true";
}
}
return url;
}
void LLFlickrConnect::connectToFlickr(const std::string& request_token, const std::string& oauth_verifier)
{
LLSD body;
if (!request_token.empty())
body["request_token"] = request_token;
if (!oauth_verifier.empty())
body["oauth_verifier"] = oauth_verifier;
LLHTTPClient::put(getFlickrConnectURL("/connection"), body, new LLFlickrConnectResponder());
}
void LLFlickrConnect::disconnectFromFlickr()
{
LLHTTPClient::del(getFlickrConnectURL("/connection"), new LLFlickrDisconnectResponder());
}
void LLFlickrConnect::checkConnectionToFlickr(bool auto_connect)
{
const bool follow_redirects = false;
const F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
LLHTTPClient::get(getFlickrConnectURL("/connection", true), new LLFlickrConnectedResponder(auto_connect),
LLSD(), timeout, follow_redirects);
}
void LLFlickrConnect::loadFlickrInfo()
{
if(mRefreshInfo)
{
const bool follow_redirects = false;
const F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
LLHTTPClient::get(getFlickrConnectURL("/info", true), new LLFlickrInfoResponder(),
LLSD(), timeout, follow_redirects);
}
}
void LLFlickrConnect::uploadPhoto(const std::string& image_url, const std::string& title, const std::string& description, const std::string& tags, int safety_level)
{
LLSD body;
body["image"] = image_url;
body["title"] = title;
body["description"] = description;
body["tags"] = tags;
body["safety_level"] = safety_level;
// Note: we can use that route for different publish action. We should be able to use the same responder.
LLHTTPClient::post(getFlickrConnectURL("/share/photo", true), body, new LLFlickrShareResponder());
}
void LLFlickrConnect::uploadPhoto(LLPointer<LLImageFormatted> image, const std::string& title, const std::string& description, const std::string& tags, int safety_level)
{
std::string imageFormat;
if (dynamic_cast<LLImagePNG*>(image.get()))
{
imageFormat = "png";
}
else if (dynamic_cast<LLImageJPEG*>(image.get()))
{
imageFormat = "jpg";
}
else
{
llwarns << "Image to upload is not a PNG or JPEG" << llendl;
return;
}
// All this code is mostly copied from LLWebProfile::post()
const std::string boundary = "----------------------------0123abcdefab";
LLSD headers;
headers["Content-Type"] = "multipart/form-data; boundary=" + boundary;
std::ostringstream body;
// *NOTE: The order seems to matter.
body << "--" << boundary << "\r\n"
<< "Content-Disposition: form-data; name=\"title\"\r\n\r\n"
<< title << "\r\n";
body << "--" << boundary << "\r\n"
<< "Content-Disposition: form-data; name=\"description\"\r\n\r\n"
<< description << "\r\n";
body << "--" << boundary << "\r\n"
<< "Content-Disposition: form-data; name=\"tags\"\r\n\r\n"
<< tags << "\r\n";
body << "--" << boundary << "\r\n"
<< "Content-Disposition: form-data; name=\"safety_level\"\r\n\r\n"
<< safety_level << "\r\n";
body << "--" << boundary << "\r\n"
<< "Content-Disposition: form-data; name=\"image\"; filename=\"Untitled." << imageFormat << "\"\r\n"
<< "Content-Type: image/" << imageFormat << "\r\n\r\n";
// Insert the image data.
// *FIX: Treating this as a string will probably screw it up ...
U8* image_data = image->getData();
for (S32 i = 0; i < image->getDataSize(); ++i)
{
body << image_data[i];
}
body << "\r\n--" << boundary << "--\r\n";
// postRaw() takes ownership of the buffer and releases it later.
size_t size = body.str().size();
U8 *data = new U8[size];
memcpy(data, body.str().data(), size);
// Note: we can use that route for different publish action. We should be able to use the same responder.
LLHTTPClient::postRaw(getFlickrConnectURL("/share/photo", true), data, size, new LLFlickrShareResponder(), headers);
}
void LLFlickrConnect::storeInfo(const LLSD& info)
{
mInfo = info;
mRefreshInfo = false;
sInfoWatcher->post(info);
}
const LLSD& LLFlickrConnect::getInfo() const
{
return mInfo;
}
void LLFlickrConnect::clearInfo()
{
mInfo = LLSD();
}
void LLFlickrConnect::setDataDirty()
{
mRefreshInfo = true;
}
void LLFlickrConnect::setConnectionState(LLFlickrConnect::EConnectionState connection_state)
{
if(connection_state == FLICKR_CONNECTED)
{
mReadFromMaster = true;
setConnected(true);
setDataDirty();
}
else if(connection_state == FLICKR_NOT_CONNECTED)
{
setConnected(false);
}
else if(connection_state == FLICKR_POSTED)
{
mReadFromMaster = false;
}
if (mConnectionState != connection_state)
{
// set the connection state before notifying watchers
mConnectionState = connection_state;
LLSD state_info;
state_info["enum"] = connection_state;
sStateWatcher->post(state_info);
}
}
void LLFlickrConnect::setConnected(bool connected)
{
mConnected = connected;
}

View File

@ -0,0 +1,98 @@
/**
* @file llflickrconnect.h
* @author Merov, Cho
* @brief Connection to Flickr Service
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, 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_LLFLICKRCONNECT_H
#define LL_LLFLICKRCONNECT_H
#include "llsingleton.h"
#include "llimage.h"
class LLEventPump;
/**
* @class LLFlickrConnect
*
* Manages authentication to, and interaction with, a web service allowing the
* the viewer to upload photos to Flickr.
*/
class LLFlickrConnect : public LLSingleton<LLFlickrConnect>
{
LOG_CLASS(LLFlickrConnect);
public:
enum EConnectionState
{
FLICKR_NOT_CONNECTED = 0,
FLICKR_CONNECTION_IN_PROGRESS = 1,
FLICKR_CONNECTED = 2,
FLICKR_CONNECTION_FAILED = 3,
FLICKR_POSTING = 4,
FLICKR_POSTED = 5,
FLICKR_POST_FAILED = 6,
FLICKR_DISCONNECTING = 7,
FLICKR_DISCONNECT_FAILED = 8
};
void connectToFlickr(const std::string& request_token = "", const std::string& oauth_verifier = ""); // Initiate the complete Flickr connection. Please use checkConnectionToFlickr() in normal use.
void disconnectFromFlickr(); // Disconnect from the Flickr service.
void checkConnectionToFlickr(bool auto_connect = false); // Check if an access token is available on the Flickr service. If not, call connectToFlickr().
void loadFlickrInfo();
void uploadPhoto(const std::string& image_url, const std::string& title, const std::string& description, const std::string& tags, int safety_level);
void uploadPhoto(LLPointer<LLImageFormatted> image, const std::string& title, const std::string& description, const std::string& tags, int safety_level);
void storeInfo(const LLSD& info);
const LLSD& getInfo() const;
void clearInfo();
void setDataDirty();
void setConnectionState(EConnectionState connection_state);
void setConnected(bool connected);
bool isConnected() { return mConnected; }
bool isTransactionOngoing() { return ((mConnectionState == FLICKR_CONNECTION_IN_PROGRESS) || (mConnectionState == FLICKR_POSTING) || (mConnectionState == FLICKR_DISCONNECTING)); }
EConnectionState getConnectionState() { return mConnectionState; }
void openFlickrWeb(std::string url);
private:
friend class LLSingleton<LLFlickrConnect>;
LLFlickrConnect();
~LLFlickrConnect() {};
std::string getFlickrConnectURL(const std::string& route = "", bool include_read_from_master = false);
EConnectionState mConnectionState;
BOOL mConnected;
LLSD mInfo;
bool mRefreshInfo;
bool mReadFromMaster;
static boost::scoped_ptr<LLEventPump> sStateWatcher;
static boost::scoped_ptr<LLEventPump> sInfoWatcher;
static boost::scoped_ptr<LLEventPump> sContentWatcher;
};
#endif // LL_LLFLICKRCONNECT_H

View File

@ -0,0 +1,110 @@
/**
* @file llfloaterbigpreview.cpp
* @brief Display of extended (big) preview for snapshots and SL Share
* @author merov@lindenlab.com
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "llfloaterbigpreview.h"
#include "llsnapshotlivepreview.h"
///////////////////////
//LLFloaterBigPreview//
///////////////////////
LLFloaterBigPreview::LLFloaterBigPreview(const LLSD& key) : LLFloater(key),
mPreviewPlaceholder(NULL),
mFloaterOwner(NULL)
{
}
LLFloaterBigPreview::~LLFloaterBigPreview()
{
if (mPreviewHandle.get())
{
mPreviewHandle.get()->die();
}
}
void LLFloaterBigPreview::onCancel()
{
closeFloater();
}
void LLFloaterBigPreview::closeOnFloaterOwnerClosing(LLFloater* floaterp)
{
if (isFloaterOwner(floaterp))
{
closeFloater();
}
}
BOOL LLFloaterBigPreview::postBuild()
{
mPreviewPlaceholder = getChild<LLUICtrl>("big_preview_placeholder");
return LLFloater::postBuild();
}
void LLFloaterBigPreview::draw()
{
LLFloater::draw();
LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get());
// Display the preview if one is available
if (previewp && previewp->getBigThumbnailImage())
{
// Get the preview rect
const LLRect& preview_rect = mPreviewPlaceholder->getRect();
// Get the preview texture size
S32 thumbnail_w = previewp->getBigThumbnailWidth();
S32 thumbnail_h = previewp->getBigThumbnailHeight();
// Compute the scaling ratio and the size of the final texture in the rect: we want to prevent anisotropic scaling (distorted in x and y)
F32 ratio = llmax((F32)(thumbnail_w)/(F32)(preview_rect.getWidth()), (F32)(thumbnail_h)/(F32)(preview_rect.getHeight()));
thumbnail_w = (S32)((F32)(thumbnail_w)/ratio);
thumbnail_h = (S32)((F32)(thumbnail_h)/ratio);
// Compute the preview offset within the preview rect: we want to center that preview in the available rect
const S32 local_offset_x = (preview_rect.getWidth() - thumbnail_w) / 2 ;
const S32 local_offset_y = (preview_rect.getHeight() - thumbnail_h) / 2 ;
// Compute preview offset within the floater rect
S32 offset_x = preview_rect.mLeft + local_offset_x;
S32 offset_y = preview_rect.mBottom + local_offset_y;
gGL.matrixMode(LLRender::MM_MODELVIEW);
// Apply floater transparency to the texture unless the floater is focused.
F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency();
LLColor4 color = LLColor4::white;
// Draw the preview texture
gl_draw_scaled_image(offset_x, offset_y,
thumbnail_w, thumbnail_h,
previewp->getBigThumbnailImage(), color % alpha);
}
}

View File

@ -0,0 +1,54 @@
/**
* @file llfloaterbigpreview.h
* @brief Display of extended (big) preview for snapshots and SL Share
* @author merov@lindenlab.com
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, 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_LLFLOATERBIGPREVIEW_H
#define LL_LLFLOATERBIGPREVIEW_H
#include "llfloater.h"
class LLFloaterBigPreview : public LLFloater
{
public:
LLFloaterBigPreview(const LLSD& key);
~LLFloaterBigPreview();
BOOL postBuild();
void draw();
void onCancel();
void setPreview(LLView* previewp) { mPreviewHandle = previewp->getHandle(); }
void setFloaterOwner(LLFloater* floaterp) { mFloaterOwner = floaterp; }
bool isFloaterOwner(LLFloater* floaterp) const { return (mFloaterOwner == floaterp); }
void closeOnFloaterOwnerClosing(LLFloater* floaterp);
private:
LLHandle<LLView> mPreviewHandle;
LLUICtrl* mPreviewPlaceholder;
LLFloater* mFloaterOwner;
};
#endif // LL_LLFLOATERBIGPREVIEW_H

View File

@ -1,6 +1,6 @@
/**
* @file llfloatersocial.h
* @brief Header file for llfloatersocial
* @file llfloaterfacebook.h
* @brief Header file for llfloaterfacebook
* @author Gilbert@lindenlab.com
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
@ -24,9 +24,10 @@
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLFLOATERSOCIAL_H
#define LL_LLFLOATERSOCIAL_H
#ifndef LL_LLFLOATERFACEBOOK_H
#define LL_LLFLOATERFACEBOOK_H
#include "llcallingcard.h"
#include "llfloater.h"
#include "lltextbox.h"
#include "llviewertexture.h"
@ -34,11 +35,13 @@
class LLIconCtrl;
class LLCheckBoxCtrl;
class LLSnapshotLivePreview;
class LLAvatarList;
class LLFloaterBigPreview;
class LLSocialStatusPanel : public LLPanel
class LLFacebookStatusPanel : public LLPanel
{
public:
LLSocialStatusPanel();
LLFacebookStatusPanel();
BOOL postBuild();
void draw();
void onSend();
@ -53,19 +56,21 @@ private:
LLUICtrl* mCancelButton;
};
class LLSocialPhotoPanel : public LLPanel
class LLFacebookPhotoPanel : public LLPanel
{
public:
LLSocialPhotoPanel();
~LLSocialPhotoPanel();
LLFacebookPhotoPanel();
~LLFacebookPhotoPanel();
BOOL postBuild();
void draw();
LLSnapshotLivePreview* getPreviewView();
void onVisibilityChanged(const LLSD& new_visibility);
void onVisibilityChange(BOOL new_visibility);
void onClickBigPreview();
void onClickNewSnapshot();
void onSend();
S32 notify(const LLSD& info);
bool onFacebookConnectStateChange(const LLSD& data);
void sendPhoto();
@ -77,22 +82,31 @@ public:
LLUICtrl* getRefreshBtn();
private:
bool isPreviewVisible();
void attachPreview();
LLHandle<LLView> mPreviewHandle;
LLUICtrl * mSnapshotPanel;
LLUICtrl * mResolutionComboBox;
LLUICtrl * mFilterComboBox;
LLUICtrl * mRefreshBtn;
LLUICtrl * mWorkingLabel;
LLUICtrl * mThumbnailPlaceholder;
LLUICtrl * mCaptionTextBox;
LLUICtrl * mPostButton;
LLUICtrl* mCancelButton;
LLUICtrl * mCancelButton;
LLButton * mBtnPreview;
LLFloaterBigPreview * mBigPreviewFloater;
S32 mQuality; // Compression quality
};
class LLSocialCheckinPanel : public LLPanel
class LLFacebookCheckinPanel : public LLPanel
{
public:
LLSocialCheckinPanel();
LLFacebookCheckinPanel();
BOOL postBuild();
void draw();
void onSend();
@ -114,15 +128,34 @@ private:
bool mReloadingMapTexture;
};
class LLSocialAccountPanel : public LLPanel
class LLFacebookFriendsPanel : public LLPanel, public LLFriendObserver
{
public:
LLSocialAccountPanel();
LLFacebookFriendsPanel();
~LLFacebookFriendsPanel();
BOOL postBuild();
virtual void changed(U32 mask);
private:
bool updateSuggestedFriendList();
void showFriendsAccordionsIfNeeded();
void updateFacebookList(bool visible);
bool onConnectedToFacebook(const LLSD& data);
LLTextBox * mFriendsStatusCaption;
LLAvatarList* mSecondLifeFriends;
LLAvatarList* mSuggestedFriends;
};
class LLFacebookAccountPanel : public LLPanel
{
public:
LLFacebookAccountPanel();
BOOL postBuild();
void draw();
private:
void onVisibilityChanged(const LLSD& new_visibility);
void onVisibilityChange(BOOL new_visibility);
bool onFacebookConnectStateChange(const LLSD& data);
bool onFacebookConnectInfoChange();
void onConnect();
@ -141,24 +174,23 @@ private:
LLUICtrl * mDisconnectButton;
};
class LLFloaterSocial : public LLFloater
class LLFloaterFacebook : public LLFloater
{
public:
LLFloaterSocial(const LLSD& key);
LLFloaterFacebook(const LLSD& key);
BOOL postBuild();
void draw();
void onClose(bool app_quitting);
void onCancel();
static void preUpdate();
static void postUpdate();
void showPhotoPanel();
private:
LLSocialPhotoPanel* mSocialPhotoPanel;
LLFacebookPhotoPanel* mFacebookPhotoPanel;
LLTextBox* mStatusErrorText;
LLTextBox* mStatusLoadingText;
LLUICtrl* mStatusLoadingIndicator;
};
#endif // LL_LLFLOATERSOCIAL_H
#endif // LL_LLFLOATERFACEBOOK_H

View File

@ -0,0 +1,798 @@
/**
* @file llfloaterflickr.cpp
* @brief Implementation of llfloaterflickr
* @author cho@lindenlab.com
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "llfloaterflickr.h"
#include "llagent.h"
#include "llagentui.h"
#include "llcheckboxctrl.h"
#include "llcombobox.h"
#include "llflickrconnect.h"
#include "llfloaterreg.h"
#include "lliconctrl.h"
#include "llimagefiltersmanager.h"
#include "llresmgr.h" // LLLocale
#include "llsdserialize.h"
#include "llloadingindicator.h"
#include "llplugincookiestore.h"
#include "llslurl.h"
#include "lltrans.h"
#include "llsnapshotlivepreview.h"
#include "llfloaterbigpreview.h"
#include "llviewerregion.h"
#include "llviewercontrol.h"
#include "llviewermedia.h"
#include "lltabcontainer.h"
#include "llviewerparcelmgr.h"
#include "llviewerregion.h"
static LLPanelInjector<LLFlickrPhotoPanel> t_panel_photo("llflickrphotopanel");
static LLPanelInjector<LLFlickrAccountPanel> t_panel_account("llflickraccountpanel");
const S32 MAX_POSTCARD_DATASIZE = 1024 * 1024; // one megabyte
const std::string DEFAULT_PHOTO_QUERY_PARAMETERS = "?sourceid=slshare_photo&utm_source=flickr&utm_medium=photo&utm_campaign=slshare";
const std::string DEFAULT_TAG_TEXT = "secondlife ";
const std::string FLICKR_MACHINE_TAGS_NAMESPACE = "secondlife";
///////////////////////////
//LLFlickrPhotoPanel///////
///////////////////////////
LLFlickrPhotoPanel::LLFlickrPhotoPanel() :
mSnapshotPanel(NULL),
mResolutionComboBox(NULL),
mRefreshBtn(NULL),
mBtnPreview(NULL),
mWorkingLabel(NULL),
mThumbnailPlaceholder(NULL),
mTitleTextBox(NULL),
mDescriptionTextBox(NULL),
mLocationCheckbox(NULL),
mTagsTextBox(NULL),
mRatingComboBox(NULL),
mBigPreviewFloater(NULL),
mPostButton(NULL)
{
mCommitCallbackRegistrar.add("SocialSharing.SendPhoto", boost::bind(&LLFlickrPhotoPanel::onSend, this));
mCommitCallbackRegistrar.add("SocialSharing.RefreshPhoto", boost::bind(&LLFlickrPhotoPanel::onClickNewSnapshot, this));
mCommitCallbackRegistrar.add("SocialSharing.BigPreview", boost::bind(&LLFlickrPhotoPanel::onClickBigPreview, this));
}
LLFlickrPhotoPanel::~LLFlickrPhotoPanel()
{
if(mPreviewHandle.get())
{
mPreviewHandle.get()->die();
}
}
BOOL LLFlickrPhotoPanel::postBuild()
{
setVisibleCallback(boost::bind(&LLFlickrPhotoPanel::onVisibilityChange, this, _2));
mSnapshotPanel = getChild<LLUICtrl>("snapshot_panel");
mResolutionComboBox = getChild<LLUICtrl>("resolution_combobox");
mResolutionComboBox->setCommitCallback(boost::bind(&LLFlickrPhotoPanel::updateResolution, this, TRUE));
mFilterComboBox = getChild<LLUICtrl>("filters_combobox");
mFilterComboBox->setCommitCallback(boost::bind(&LLFlickrPhotoPanel::updateResolution, this, TRUE));
mRefreshBtn = getChild<LLUICtrl>("new_snapshot_btn");
mBtnPreview = getChild<LLButton>("big_preview_btn");
mWorkingLabel = getChild<LLUICtrl>("working_lbl");
mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder");
mTitleTextBox = getChild<LLUICtrl>("photo_title");
mDescriptionTextBox = getChild<LLUICtrl>("photo_description");
mLocationCheckbox = getChild<LLUICtrl>("add_location_cb");
mTagsTextBox = getChild<LLUICtrl>("photo_tags");
mTagsTextBox->setValue(DEFAULT_TAG_TEXT);
mRatingComboBox = getChild<LLUICtrl>("rating_combobox");
mPostButton = getChild<LLUICtrl>("post_photo_btn");
mCancelButton = getChild<LLUICtrl>("cancel_photo_btn");
mBigPreviewFloater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
// Update filter list
std::vector<std::string> filter_list = LLImageFiltersManager::getInstance()->getFiltersList();
LLComboBox* filterbox = static_cast<LLComboBox *>(mFilterComboBox);
for (U32 i = 0; i < filter_list.size(); i++)
{
filterbox->add(filter_list[i]);
}
return LLPanel::postBuild();
}
// virtual
S32 LLFlickrPhotoPanel::notify(const LLSD& info)
{
if (info.has("snapshot-updating"))
{
// Disable the Post button and whatever else while the snapshot is not updated
// updateControls();
return 1;
}
if (info.has("snapshot-updated"))
{
// Enable the send/post/save buttons.
updateControls();
// The refresh button is initially hidden. We show it after the first update,
// i.e. after snapshot is taken
LLUICtrl * refresh_button = getRefreshBtn();
if (!refresh_button->getVisible())
{
refresh_button->setVisible(true);
}
return 1;
}
return 0;
}
void LLFlickrPhotoPanel::draw()
{
LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get());
// Enable interaction only if no transaction with the service is on-going (prevent duplicated posts)
bool no_ongoing_connection = !(LLFlickrConnect::instance().isTransactionOngoing());
mCancelButton->setEnabled(no_ongoing_connection);
mTitleTextBox->setEnabled(no_ongoing_connection);
mDescriptionTextBox->setEnabled(no_ongoing_connection);
mTagsTextBox->setEnabled(no_ongoing_connection);
mRatingComboBox->setEnabled(no_ongoing_connection);
mResolutionComboBox->setEnabled(no_ongoing_connection);
mFilterComboBox->setEnabled(no_ongoing_connection);
mRefreshBtn->setEnabled(no_ongoing_connection);
mBtnPreview->setEnabled(no_ongoing_connection);
mLocationCheckbox->setEnabled(no_ongoing_connection);
// Reassign the preview floater if we have the focus and the preview exists
if (hasFocus() && isPreviewVisible())
{
attachPreview();
}
// Toggle the button state as appropriate
bool preview_active = (isPreviewVisible() && mBigPreviewFloater->isFloaterOwner(getParentByType<LLFloater>()));
mBtnPreview->setToggleState(preview_active);
// Display the preview if one is available
if (previewp && previewp->getThumbnailImage())
{
const LLRect& thumbnail_rect = mThumbnailPlaceholder->getRect();
const S32 thumbnail_w = previewp->getThumbnailWidth();
const S32 thumbnail_h = previewp->getThumbnailHeight();
// calc preview offset within the preview rect
const S32 local_offset_x = (thumbnail_rect.getWidth() - thumbnail_w) / 2 ;
const S32 local_offset_y = (thumbnail_rect.getHeight() - thumbnail_h) / 2 ;
// calc preview offset within the floater rect
// Hack : To get the full offset, we need to take into account each and every offset of each widgets up to the floater.
// This is almost as arbitrary as using a fixed offset so that's what we do here for the sake of simplicity.
// *TODO : Get the offset looking through the hierarchy of widgets, should be done in postBuild() so to avoid traversing the hierarchy each time.
S32 offset_x = thumbnail_rect.mLeft + local_offset_x - 1;
S32 offset_y = thumbnail_rect.mBottom + local_offset_y - 39;
mSnapshotPanel->localPointToOtherView(offset_x, offset_y, &offset_x, &offset_y, getParentByType<LLFloater>());
gGL.matrixMode(LLRender::MM_MODELVIEW);
// Apply floater transparency to the texture unless the floater is focused.
F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency();
LLColor4 color = LLColor4::white;
gl_draw_scaled_image(offset_x, offset_y,
thumbnail_w, thumbnail_h,
previewp->getThumbnailImage(), color % alpha);
}
// Update the visibility of the working (computing preview) label
mWorkingLabel->setVisible(!(previewp && previewp->getSnapshotUpToDate()));
// Enable Post if we have a preview to send and no on going connection being processed
mPostButton->setEnabled(no_ongoing_connection && (previewp && previewp->getSnapshotUpToDate()));
// Draw the rest of the panel on top of it
LLPanel::draw();
}
LLSnapshotLivePreview* LLFlickrPhotoPanel::getPreviewView()
{
LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)mPreviewHandle.get();
return previewp;
}
void LLFlickrPhotoPanel::onVisibilityChange(BOOL visible)
{
if (visible)
{
if (mPreviewHandle.get())
{
LLSnapshotLivePreview* preview = getPreviewView();
if(preview)
{
lldebugs << "opened, updating snapshot" << llendl;
preview->updateSnapshot(TRUE);
}
}
else
{
LLRect full_screen_rect = getRootView()->getRect();
LLSnapshotLivePreview::Params p;
p.rect(full_screen_rect);
LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p);
mPreviewHandle = previewp->getHandle();
previewp->setContainer(this);
previewp->setSnapshotType(previewp->SNAPSHOT_WEB);
previewp->setSnapshotFormat(LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG);
previewp->setThumbnailSubsampled(TRUE); // We want the preview to reflect the *saved* image
previewp->setAllowRenderUI(FALSE); // We do not want the rendered UI in our snapshots
previewp->setAllowFullScreenPreview(FALSE); // No full screen preview in SL Share mode
previewp->setThumbnailPlaceholderRect(mThumbnailPlaceholder->getRect());
updateControls();
}
}
}
void LLFlickrPhotoPanel::onClickNewSnapshot()
{
LLSnapshotLivePreview* previewp = getPreviewView();
if (previewp)
{
previewp->updateSnapshot(TRUE);
}
}
void LLFlickrPhotoPanel::onClickBigPreview()
{
// Toggle the preview
if (isPreviewVisible())
{
LLFloaterReg::hideInstance("big_preview");
}
else
{
attachPreview();
LLFloaterReg::showInstance("big_preview");
}
}
bool LLFlickrPhotoPanel::isPreviewVisible()
{
return (mBigPreviewFloater && mBigPreviewFloater->getVisible());
}
void LLFlickrPhotoPanel::attachPreview()
{
if (mBigPreviewFloater)
{
LLSnapshotLivePreview* previewp = getPreviewView();
mBigPreviewFloater->setPreview(previewp);
mBigPreviewFloater->setFloaterOwner(getParentByType<LLFloater>());
}
}
void LLFlickrPhotoPanel::onSend()
{
LLEventPumps::instance().obtain("FlickrConnectState").stopListening("LLFlickrPhotoPanel"); // just in case it is already listening
LLEventPumps::instance().obtain("FlickrConnectState").listen("LLFlickrPhotoPanel", boost::bind(&LLFlickrPhotoPanel::onFlickrConnectStateChange, this, _1));
// Connect to Flickr if necessary and then post
if (LLFlickrConnect::instance().isConnected())
{
sendPhoto();
}
else
{
LLFlickrConnect::instance().checkConnectionToFlickr(true);
}
}
bool LLFlickrPhotoPanel::onFlickrConnectStateChange(const LLSD& data)
{
switch (data.get("enum").asInteger())
{
case LLFlickrConnect::FLICKR_CONNECTED:
sendPhoto();
break;
case LLFlickrConnect::FLICKR_POSTED:
LLEventPumps::instance().obtain("FlickrConnectState").stopListening("LLFlickrPhotoPanel");
clearAndClose();
break;
}
return false;
}
void LLFlickrPhotoPanel::sendPhoto()
{
// Get the title, description, and tags
std::string title = mTitleTextBox->getValue().asString();
std::string description = mDescriptionTextBox->getValue().asString();
std::string tags = mTagsTextBox->getValue().asString();
// Add the location if required
bool add_location = mLocationCheckbox->getValue().asBoolean();
if (add_location)
{
// Get the SLURL for the location
LLSLURL slurl;
LLAgentUI::buildSLURL(slurl);
std::string slurl_string = slurl.getSLURLString();
// Add query parameters so Google Analytics can track incoming clicks!
slurl_string += DEFAULT_PHOTO_QUERY_PARAMETERS;
std::string photo_link_text = "Visit this location";// at [] in Second Life";
std::string parcel_name = LLViewerParcelMgr::getInstance()->getAgentParcelName();
if (!parcel_name.empty())
{
photo_link_text += " at " + parcel_name;
}
photo_link_text += " in Second Life";
slurl_string = "<a href=\"" + slurl_string + "\">" + photo_link_text + "</a>";
// Add it to the description (pretty crude, but we don't have a better option with photos)
if (description.empty())
description = slurl_string;
else
description = description + "\n\n" + slurl_string;
// Also add special "machine tags" with location metadata
const LLVector3& agent_pos_region = gAgent.getPositionAgent();
LLViewerRegion* region = gAgent.getRegion();
LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
if (region && parcel)
{
S32 pos_x = S32(agent_pos_region.mV[VX]);
S32 pos_y = S32(agent_pos_region.mV[VY]);
S32 pos_z = S32(agent_pos_region.mV[VZ]);
std::string parcel_name = LLViewerParcelMgr::getInstance()->getAgentParcelName();
std::string region_name = region->getName();
if (!region_name.empty())
{
tags += llformat(" \"%s:region=%s\"", FLICKR_MACHINE_TAGS_NAMESPACE.c_str(), region_name.c_str());
}
if (!parcel_name.empty())
{
tags += llformat(" \"%s:parcel=%s\"", FLICKR_MACHINE_TAGS_NAMESPACE.c_str(), parcel_name.c_str());
}
tags += llformat(" \"%s:x=%d\"", FLICKR_MACHINE_TAGS_NAMESPACE.c_str(), pos_x);
tags += llformat(" \"%s:y=%d\"", FLICKR_MACHINE_TAGS_NAMESPACE.c_str(), pos_y);
tags += llformat(" \"%s:z=%d\"", FLICKR_MACHINE_TAGS_NAMESPACE.c_str(), pos_z);
}
}
// Get the content rating
int content_rating = mRatingComboBox->getValue().asInteger();
// Get the image
LLSnapshotLivePreview* previewp = getPreviewView();
// Post to Flickr
LLFlickrConnect::instance().uploadPhoto(previewp->getFormattedImage(), title, description, tags, content_rating);
updateControls();
}
void LLFlickrPhotoPanel::clearAndClose()
{
mTitleTextBox->setValue("");
mDescriptionTextBox->setValue("");
LLFloater* floater = getParentByType<LLFloater>();
if (floater)
{
floater->closeFloater();
if (mBigPreviewFloater)
{
mBigPreviewFloater->closeOnFloaterOwnerClosing(floater);
}
}
}
void LLFlickrPhotoPanel::updateControls()
{
LLSnapshotLivePreview* previewp = getPreviewView();
BOOL got_snap = previewp && previewp->getSnapshotUpToDate();
// *TODO: Separate maximum size for Web images from postcards
lldebugs << "Is snapshot up-to-date? " << got_snap << llendl;
updateResolution(FALSE);
}
void LLFlickrPhotoPanel::updateResolution(BOOL do_update)
{
LLComboBox* combobox = static_cast<LLComboBox *>(mResolutionComboBox);
LLComboBox* filterbox = static_cast<LLComboBox *>(mFilterComboBox);
std::string sdstring = combobox->getSelectedValue();
LLSD sdres;
std::stringstream sstream(sdstring);
LLSDSerialize::fromNotation(sdres, sstream, sdstring.size());
S32 width = sdres[0];
S32 height = sdres[1];
// Note : index 0 of the filter drop down is assumed to be "No filter" in whichever locale
std::string filter_name = (filterbox->getCurrentIndex() ? filterbox->getSimple() : "");
LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get());
if (previewp && combobox->getCurrentIndex() >= 0)
{
S32 original_width = 0 , original_height = 0 ;
previewp->getSize(original_width, original_height) ;
if (width == 0 || height == 0)
{
// take resolution from current window size
lldebugs << "Setting preview res from window: " << gViewerWindow->getWindowWidthRaw() << "x" << gViewerWindow->getWindowHeightRaw() << llendl;
previewp->setSize(gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw());
}
else
{
// use the resolution from the selected pre-canned drop-down choice
lldebugs << "Setting preview res selected from combo: " << width << "x" << height << llendl;
previewp->setSize(width, height);
}
checkAspectRatio(width);
previewp->getSize(width, height);
if ((original_width != width) || (original_height != height))
{
previewp->setSize(width, height);
if (do_update)
{
previewp->updateSnapshot(TRUE);
updateControls();
}
}
// Get the old filter, compare to the current one "filter_name" and set if changed
std::string original_filter = previewp->getFilter();
if (original_filter != filter_name)
{
previewp->setFilter(filter_name);
if (do_update)
{
previewp->updateSnapshot(FALSE, TRUE);
updateControls();
}
}
}
}
void LLFlickrPhotoPanel::checkAspectRatio(S32 index)
{
LLSnapshotLivePreview *previewp = getPreviewView() ;
BOOL keep_aspect = FALSE;
if (0 == index) // current window size
{
keep_aspect = TRUE;
}
else // predefined resolution
{
keep_aspect = FALSE;
}
if (previewp)
{
previewp->mKeepAspectRatio = keep_aspect;
}
}
LLUICtrl* LLFlickrPhotoPanel::getRefreshBtn()
{
return mRefreshBtn;
}
///////////////////////////
//LLFlickrAccountPanel//////
///////////////////////////
LLFlickrAccountPanel::LLFlickrAccountPanel() :
mAccountCaptionLabel(NULL),
mAccountNameLabel(NULL),
mPanelButtons(NULL),
mConnectButton(NULL),
mDisconnectButton(NULL)
{
mCommitCallbackRegistrar.add("SocialSharing.Connect", boost::bind(&LLFlickrAccountPanel::onConnect, this));
mCommitCallbackRegistrar.add("SocialSharing.Disconnect", boost::bind(&LLFlickrAccountPanel::onDisconnect, this));
setVisibleCallback(boost::bind(&LLFlickrAccountPanel::onVisibilityChange, this, _2));
}
BOOL LLFlickrAccountPanel::postBuild()
{
mAccountCaptionLabel = getChild<LLTextBox>("account_caption_label");
mAccountNameLabel = getChild<LLTextBox>("account_name_label");
mPanelButtons = getChild<LLUICtrl>("panel_buttons");
mConnectButton = getChild<LLUICtrl>("connect_btn");
mDisconnectButton = getChild<LLUICtrl>("disconnect_btn");
return LLPanel::postBuild();
}
void LLFlickrAccountPanel::draw()
{
LLFlickrConnect::EConnectionState connection_state = LLFlickrConnect::instance().getConnectionState();
//Disable the 'disconnect' button and the 'use another account' button when disconnecting in progress
bool disconnecting = connection_state == LLFlickrConnect::FLICKR_DISCONNECTING;
mDisconnectButton->setEnabled(!disconnecting);
//Disable the 'connect' button when a connection is in progress
bool connecting = connection_state == LLFlickrConnect::FLICKR_CONNECTION_IN_PROGRESS;
mConnectButton->setEnabled(!connecting);
LLPanel::draw();
}
void LLFlickrAccountPanel::onVisibilityChange(BOOL visible)
{
if(visible)
{
LLEventPumps::instance().obtain("FlickrConnectState").stopListening("LLFlickrAccountPanel");
LLEventPumps::instance().obtain("FlickrConnectState").listen("LLFlickrAccountPanel", boost::bind(&LLFlickrAccountPanel::onFlickrConnectStateChange, this, _1));
LLEventPumps::instance().obtain("FlickrConnectInfo").stopListening("LLFlickrAccountPanel");
LLEventPumps::instance().obtain("FlickrConnectInfo").listen("LLFlickrAccountPanel", boost::bind(&LLFlickrAccountPanel::onFlickrConnectInfoChange, this));
//Connected
if(LLFlickrConnect::instance().isConnected())
{
showConnectedLayout();
}
//Check if connected (show disconnected layout in meantime)
else
{
showDisconnectedLayout();
}
if ((LLFlickrConnect::instance().getConnectionState() == LLFlickrConnect::FLICKR_NOT_CONNECTED) ||
(LLFlickrConnect::instance().getConnectionState() == LLFlickrConnect::FLICKR_CONNECTION_FAILED))
{
LLFlickrConnect::instance().checkConnectionToFlickr();
}
}
else
{
LLEventPumps::instance().obtain("FlickrConnectState").stopListening("LLFlickrAccountPanel");
LLEventPumps::instance().obtain("FlickrConnectInfo").stopListening("LLFlickrAccountPanel");
}
}
bool LLFlickrAccountPanel::onFlickrConnectStateChange(const LLSD& data)
{
if(LLFlickrConnect::instance().isConnected())
{
//In process of disconnecting so leave the layout as is
if(data.get("enum").asInteger() != LLFlickrConnect::FLICKR_DISCONNECTING)
{
showConnectedLayout();
}
}
else
{
showDisconnectedLayout();
}
return false;
}
bool LLFlickrAccountPanel::onFlickrConnectInfoChange()
{
LLSD info = LLFlickrConnect::instance().getInfo();
std::string clickable_name;
//Strings of format [http://www.somewebsite.com Click Me] become clickable text
if(info.has("link") && info.has("name"))
{
clickable_name = "[" + info["link"].asString() + " " + info["name"].asString() + "]";
}
mAccountNameLabel->setText(clickable_name);
return false;
}
void LLFlickrAccountPanel::showConnectButton()
{
if(!mConnectButton->getVisible())
{
mConnectButton->setVisible(TRUE);
mDisconnectButton->setVisible(FALSE);
}
}
void LLFlickrAccountPanel::hideConnectButton()
{
if(mConnectButton->getVisible())
{
mConnectButton->setVisible(FALSE);
mDisconnectButton->setVisible(TRUE);
}
}
void LLFlickrAccountPanel::showDisconnectedLayout()
{
mAccountCaptionLabel->setText(getString("flickr_disconnected"));
mAccountNameLabel->setText(std::string(""));
showConnectButton();
}
void LLFlickrAccountPanel::showConnectedLayout()
{
LLFlickrConnect::instance().loadFlickrInfo();
mAccountCaptionLabel->setText(getString("flickr_connected"));
hideConnectButton();
}
void LLFlickrAccountPanel::onConnect()
{
LLFlickrConnect::instance().checkConnectionToFlickr(true);
//Clear only the flickr browser cookies so that the flickr login screen appears
LLViewerMedia::getCookieStore()->removeCookiesByDomain(".flickr.com");
}
void LLFlickrAccountPanel::onDisconnect()
{
LLFlickrConnect::instance().disconnectFromFlickr();
LLViewerMedia::getCookieStore()->removeCookiesByDomain(".flickr.com");
}
////////////////////////
//LLFloaterFlickr///////
////////////////////////
LLFloaterFlickr::LLFloaterFlickr(const LLSD& key) : LLFloater(key),
mFlickrPhotoPanel(NULL),
mStatusErrorText(NULL),
mStatusLoadingText(NULL),
mStatusLoadingIndicator(NULL)
{
mCommitCallbackRegistrar.add("SocialSharing.Cancel", boost::bind(&LLFloaterFlickr::onCancel, this));
}
void LLFloaterFlickr::onClose(bool app_quitting)
{
LLFloaterBigPreview* big_preview_floater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
if (big_preview_floater)
{
big_preview_floater->closeOnFloaterOwnerClosing(this);
}
LLFloater::onClose(app_quitting);
}
void LLFloaterFlickr::onCancel()
{
LLFloaterBigPreview* big_preview_floater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
if (big_preview_floater)
{
big_preview_floater->closeOnFloaterOwnerClosing(this);
}
closeFloater();
}
BOOL LLFloaterFlickr::postBuild()
{
// Keep tab of the Photo Panel
mFlickrPhotoPanel = static_cast<LLFlickrPhotoPanel*>(getChild<LLUICtrl>("panel_flickr_photo"));
// Connection status widgets
mStatusErrorText = getChild<LLTextBox>("connection_error_text");
mStatusLoadingText = getChild<LLTextBox>("connection_loading_text");
mStatusLoadingIndicator = getChild<LLUICtrl>("connection_loading_indicator");
return LLFloater::postBuild();
}
void LLFloaterFlickr::showPhotoPanel()
{
LLTabContainer* parent = dynamic_cast<LLTabContainer*>(mFlickrPhotoPanel->getParent());
if (!parent)
{
llwarns << "Cannot find panel container" << llendl;
return;
}
parent->selectTabPanel(mFlickrPhotoPanel);
}
void LLFloaterFlickr::draw()
{
if (mStatusErrorText && mStatusLoadingText && mStatusLoadingIndicator)
{
mStatusErrorText->setVisible(false);
mStatusLoadingText->setVisible(false);
mStatusLoadingIndicator->setVisible(false);
LLFlickrConnect::EConnectionState connection_state = LLFlickrConnect::instance().getConnectionState();
std::string status_text;
switch (connection_state)
{
case LLFlickrConnect::FLICKR_NOT_CONNECTED:
// No status displayed when first opening the panel and no connection done
case LLFlickrConnect::FLICKR_CONNECTED:
// When successfully connected, no message is displayed
case LLFlickrConnect::FLICKR_POSTED:
// No success message to show since we actually close the floater after successful posting completion
break;
case LLFlickrConnect::FLICKR_CONNECTION_IN_PROGRESS:
// Connection loading indicator
mStatusLoadingText->setVisible(true);
status_text = LLTrans::getString("SocialFlickrConnecting");
mStatusLoadingText->setValue(status_text);
mStatusLoadingIndicator->setVisible(true);
break;
case LLFlickrConnect::FLICKR_POSTING:
// Posting indicator
mStatusLoadingText->setVisible(true);
status_text = LLTrans::getString("SocialFlickrPosting");
mStatusLoadingText->setValue(status_text);
mStatusLoadingIndicator->setVisible(true);
break;
case LLFlickrConnect::FLICKR_CONNECTION_FAILED:
// Error connecting to the service
mStatusErrorText->setVisible(true);
status_text = LLTrans::getString("SocialFlickrErrorConnecting");
mStatusErrorText->setValue(status_text);
break;
case LLFlickrConnect::FLICKR_POST_FAILED:
// Error posting to the service
mStatusErrorText->setVisible(true);
status_text = LLTrans::getString("SocialFlickrErrorPosting");
mStatusErrorText->setValue(status_text);
break;
case LLFlickrConnect::FLICKR_DISCONNECTING:
// Disconnecting loading indicator
mStatusLoadingText->setVisible(true);
status_text = LLTrans::getString("SocialFlickrDisconnecting");
mStatusLoadingText->setValue(status_text);
mStatusLoadingIndicator->setVisible(true);
break;
case LLFlickrConnect::FLICKR_DISCONNECT_FAILED:
// Error disconnecting from the service
mStatusErrorText->setVisible(true);
status_text = LLTrans::getString("SocialFlickrErrorDisconnecting");
mStatusErrorText->setValue(status_text);
break;
}
}
LLFloater::draw();
}

View File

@ -0,0 +1,135 @@
/**
* @file llfloaterflickr.h
* @brief Header file for llfloaterflickr
* @author cho@lindenlab.com
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, 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_LLFLOATERFLICKR_H
#define LL_LLFLOATERFLICKR_H
#include "llfloater.h"
#include "lltextbox.h"
#include "llviewertexture.h"
class LLIconCtrl;
class LLCheckBoxCtrl;
class LLSnapshotLivePreview;
class LLFloaterBigPreview;
class LLFlickrPhotoPanel : public LLPanel
{
public:
LLFlickrPhotoPanel();
~LLFlickrPhotoPanel();
BOOL postBuild();
S32 notify(const LLSD& info);
void draw();
LLSnapshotLivePreview* getPreviewView();
void onVisibilityChange(BOOL new_visibility);
void onClickNewSnapshot();
void onClickBigPreview();
void onSend();
bool onFlickrConnectStateChange(const LLSD& data);
void sendPhoto();
void clearAndClose();
void updateControls();
void updateResolution(BOOL do_update);
void checkAspectRatio(S32 index);
LLUICtrl* getRefreshBtn();
private:
bool isPreviewVisible();
void attachPreview();
LLHandle<LLView> mPreviewHandle;
LLUICtrl * mSnapshotPanel;
LLUICtrl * mResolutionComboBox;
LLUICtrl * mFilterComboBox;
LLUICtrl * mRefreshBtn;
LLUICtrl * mWorkingLabel;
LLUICtrl * mThumbnailPlaceholder;
LLUICtrl * mTitleTextBox;
LLUICtrl * mDescriptionTextBox;
LLUICtrl * mLocationCheckbox;
LLUICtrl * mTagsTextBox;
LLUICtrl * mRatingComboBox;
LLUICtrl * mPostButton;
LLUICtrl * mCancelButton;
LLButton * mBtnPreview;
LLFloaterBigPreview * mBigPreviewFloater;
};
class LLFlickrAccountPanel : public LLPanel
{
public:
LLFlickrAccountPanel();
BOOL postBuild();
void draw();
private:
void onVisibilityChange(BOOL new_visibility);
bool onFlickrConnectStateChange(const LLSD& data);
bool onFlickrConnectInfoChange();
void onConnect();
void onUseAnotherAccount();
void onDisconnect();
void showConnectButton();
void hideConnectButton();
void showDisconnectedLayout();
void showConnectedLayout();
LLTextBox * mAccountCaptionLabel;
LLTextBox * mAccountNameLabel;
LLUICtrl * mPanelButtons;
LLUICtrl * mConnectButton;
LLUICtrl * mDisconnectButton;
};
class LLFloaterFlickr : public LLFloater
{
public:
LLFloaterFlickr(const LLSD& key);
BOOL postBuild();
void draw();
void onClose(bool app_quitting);
void onCancel();
void showPhotoPanel();
private:
LLFlickrPhotoPanel* mFlickrPhotoPanel;
LLTextBox* mStatusErrorText;
LLTextBox* mStatusLoadingText;
LLUICtrl* mStatusLoadingIndicator;
};
#endif // LL_LLFLOATERFLICKR_H

View File

@ -31,7 +31,10 @@
#include "llagent.h"
#include "llfacebookconnect.h"
#include "llfloaterreg.h"
#include "llfloatersocial.h"
#include "llfloaterfacebook.h"
#include "llfloaterflickr.h"
#include "llfloatertwitter.h"
#include "llimagefiltersmanager.h"
#include "llcheckboxctrl.h"
#include "llcombobox.h"
#include "llpostcard.h"
@ -91,6 +94,7 @@ public:
}
static void onClickNewSnapshot(void* data);
static void onClickAutoSnap(LLUICtrl *ctrl, void* data);
static void onClickFilter(LLUICtrl *ctrl, void* data);
//static void onClickAdvanceSnap(LLUICtrl *ctrl, void* data);
static void onClickMore(void* data) ;
static void onClickUICheck(LLUICtrl *ctrl, void* data);
@ -429,9 +433,8 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)
image_res_tb->setVisible(got_snap);
if (got_snap)
{
LLPointer<LLImageRaw> img = previewp->getEncodedImage();
image_res_tb->setTextArg("[WIDTH]", llformat("%d", img->getWidth()));
image_res_tb->setTextArg("[HEIGHT]", llformat("%d", img->getHeight()));
image_res_tb->setTextArg("[WIDTH]", llformat("%d", previewp->getEncodedImageWidth()));
image_res_tb->setTextArg("[HEIGHT]", llformat("%d", previewp->getEncodedImageHeight()));
}
floater->getChild<LLUICtrl>("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : floater->getString("unknown"));
@ -464,8 +467,8 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)
default:
break;
}
if (previewp)
if (previewp)
{
previewp->setSnapshotType(shot_type);
previewp->setSnapshotFormat(shot_format);
@ -558,6 +561,26 @@ void LLFloaterSnapshot::Impl::onClickAutoSnap(LLUICtrl *ctrl, void* data)
}
}
// static
void LLFloaterSnapshot::Impl::onClickFilter(LLUICtrl *ctrl, void* data)
{
LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
if (view)
{
updateControls(view);
LLSnapshotLivePreview* previewp = getPreviewView(view);
if (previewp)
{
checkAutoSnapshot(previewp);
// Note : index 0 of the filter drop down is assumed to be "No filter" in whichever locale
LLComboBox* filterbox = static_cast<LLComboBox *>(view->getChild<LLComboBox>("filters_combobox"));
std::string filter_name = (filterbox->getCurrentIndex() ? filterbox->getSimple() : "");
previewp->setFilter(filter_name);
previewp->updateSnapshot(FALSE, TRUE);
}
}
}
void LLFloaterSnapshot::Impl::onClickMore(void* data)
{
BOOL visible = gSavedSettings.getBOOL("AdvanceSnapshot");
@ -618,7 +641,7 @@ void LLFloaterSnapshot::Impl::applyKeepAspectCheck(LLFloaterSnapshot* view, BOOL
LL_DEBUGS() << "updating thumbnail" << LL_ENDL;
previewp->setSize(w, h) ;
previewp->updateSnapshot(FALSE, TRUE);
previewp->updateSnapshot(TRUE);
checkAutoSnapshot(previewp, TRUE);
}
}
@ -853,7 +876,6 @@ void LLFloaterSnapshot::Impl::onImageQualityChange(LLFloaterSnapshot* view, S32
{
previewp->setSnapshotQuality(quality_val);
}
checkAutoSnapshot(previewp, TRUE);
}
// static
@ -1052,7 +1074,26 @@ BOOL LLFloaterSnapshot::postBuild()
getChild<LLUICtrl>("auto_snapshot_check")->setValue(gSavedSettings.getBOOL("AutoSnapshot"));
childSetCommitCallback("auto_snapshot_check", Impl::onClickAutoSnap, this);
// Filters
LLComboBox* filterbox = getChild<LLComboBox>("filters_combobox");
if (gSavedSettings.getBOOL("SnapshotFiltersEnabled"))
{
// Update filter list if setting is on (experimental)
std::vector<std::string> filter_list = LLImageFiltersManager::getInstance()->getFiltersList();
for (U32 i = 0; i < filter_list.size(); i++)
{
filterbox->add(filter_list[i]);
}
childSetCommitCallback("filters_combobox", Impl::onClickFilter, this);
}
else
{
// Hide Filter UI if setting is off (default)
getChild<LLUICtrl>("filter_list_label")->setVisible(FALSE);
filterbox->setVisible(FALSE);
}
LLWebProfile::setImageUploadResultCallback(boost::bind(&LLFloaterSnapshot::Impl::onSnapshotUploadFinished, _1));
LLPostCard::setPostResultCallback(boost::bind(&LLFloaterSnapshot::Impl::onSendingPostcardFinished, _1));
@ -1082,6 +1123,7 @@ BOOL LLFloaterSnapshot::postBuild()
getChild<LLComboBox>("local_format_combo")->selectNthItem(0);
impl.mPreviewHandle = previewp->getHandle();
previewp->setContainer(this);
impl.updateControls(this);
impl.updateLayout(this);
@ -1246,6 +1288,32 @@ S32 LLFloaterSnapshot::notify(const LLSD& info)
impl.setStatus(Impl::STATUS_FINISHED, data["ok"].asBoolean(), data["msg"].asString());
return 1;
}
if (info.has("snapshot-updating"))
{
// Disable the send/post/save buttons until snapshot is ready.
impl.updateControls(this);
// Force hiding the "Refresh to save" hint because we know we've just started refresh.
impl.setNeedRefresh(this, false);
return 1;
}
if (info.has("snapshot-updated"))
{
// Enable the send/post/save buttons.
impl.updateControls(this);
// We've just done refresh.
impl.setNeedRefresh(this, false);
// The refresh button is initially hidden. We show it after the first update,
// i.e. when preview appears.
if (!mRefreshBtn->getVisible())
{
mRefreshBtn->setVisible(true);
}
return 1;
}
return 0;
}
@ -1253,9 +1321,11 @@ S32 LLFloaterSnapshot::notify(const LLSD& info)
void LLFloaterSnapshot::update()
{
LLFloaterSnapshot* inst = findInstance();
LLFloaterSocial* floater_social = LLFloaterReg::findTypedInstance<LLFloaterSocial>("social");
LLFloaterFacebook* floater_facebook = LLFloaterReg::findTypedInstance<LLFloaterFacebook>("facebook");
LLFloaterFlickr* floater_flickr = LLFloaterReg::findTypedInstance<LLFloaterFlickr>("flickr");
LLFloaterTwitter* floater_twitter = LLFloaterReg::findTypedInstance<LLFloaterTwitter>("twitter");
if (!inst && !floater_social)
if (!inst && !floater_facebook && !floater_flickr && !floater_twitter)
return;
BOOL changed = FALSE;
@ -1328,43 +1398,6 @@ BOOL LLFloaterSnapshot::saveLocal()
return previewp->saveLocal();
}
// static
void LLFloaterSnapshot::preUpdate()
{
// FIXME: duplicated code
LLFloaterSnapshot* instance = findInstance();
if (instance)
{
// Disable the send/post/save buttons until snapshot is ready.
Impl::updateControls(instance);
// Force hiding the "Refresh to save" hint because we know we've just started refresh.
Impl::setNeedRefresh(instance, false);
}
}
// static
void LLFloaterSnapshot::postUpdate()
{
// FIXME: duplicated code
LLFloaterSnapshot* instance = findInstance();
if (instance)
{
// Enable the send/post/save buttons.
Impl::updateControls(instance);
// We've just done refresh.
Impl::setNeedRefresh(instance, false);
// The refresh button is initially hidden. We show it after the first update,
// i.e. when preview appears.
if (!instance->mRefreshBtn->getVisible())
{
instance->mRefreshBtn->setVisible(true);
}
}
}
// static
void LLFloaterSnapshot::postSave()
{

View File

@ -59,8 +59,6 @@ public:
static LLFloaterSnapshot* findInstance();
static void saveTexture();
static BOOL saveLocal();
static void preUpdate();
static void postUpdate();
static void postSave();
static void postPanelSwitch();
static LLPointer<LLImageFormatted> getImageData();

View File

@ -0,0 +1,827 @@
/**
* @file llfloatertwitter.cpp
* @brief Implementation of llfloatertwitter
* @author cho@lindenlab.com
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "llfloatertwitter.h"
#include "llagent.h"
#include "llagentui.h"
#include "llcheckboxctrl.h"
#include "llcombobox.h"
#include "lltwitterconnect.h"
#include "llfloaterbigpreview.h"
#include "llfloaterreg.h"
#include "lliconctrl.h"
#include "llimagefiltersmanager.h"
#include "llresmgr.h" // LLLocale
#include "llsdserialize.h"
#include "llloadingindicator.h"
#include "llplugincookiestore.h"
#include "llslurl.h"
#include "lltrans.h"
#include "llsnapshotlivepreview.h"
#include "llviewerregion.h"
#include "llviewercontrol.h"
#include "llviewermedia.h"
#include "lltabcontainer.h"
#include "lltexteditor.h"
static LLPanelInjector<LLTwitterPhotoPanel> t_panel_photo("lltwitterphotopanel");
static LLPanelInjector<LLTwitterAccountPanel> t_panel_account("lltwitteraccountpanel");
const S32 MAX_POSTCARD_DATASIZE = 1024 * 1024; // one megabyte
const std::string DEFAULT_PHOTO_LOCATION_URL = "http://maps.secondlife.com/";
const std::string DEFAULT_PHOTO_QUERY_PARAMETERS = "?sourceid=slshare_photo&utm_source=twitter&utm_medium=photo&utm_campaign=slshare";
const std::string DEFAULT_STATUS_TEXT = " #SecondLife";
///////////////////////////
//LLTwitterPhotoPanel///////
///////////////////////////
LLTwitterPhotoPanel::LLTwitterPhotoPanel() :
mSnapshotPanel(NULL),
mResolutionComboBox(NULL),
mRefreshBtn(NULL),
mBtnPreview(NULL),
mWorkingLabel(NULL),
mThumbnailPlaceholder(NULL),
mStatusCounterLabel(NULL),
mStatusTextBox(NULL),
mLocationCheckbox(NULL),
mPhotoCheckbox(NULL),
mBigPreviewFloater(NULL),
mPostButton(NULL)
{
mCommitCallbackRegistrar.add("SocialSharing.SendPhoto", boost::bind(&LLTwitterPhotoPanel::onSend, this));
mCommitCallbackRegistrar.add("SocialSharing.RefreshPhoto", boost::bind(&LLTwitterPhotoPanel::onClickNewSnapshot, this));
mCommitCallbackRegistrar.add("SocialSharing.BigPreview", boost::bind(&LLTwitterPhotoPanel::onClickBigPreview, this));
}
LLTwitterPhotoPanel::~LLTwitterPhotoPanel()
{
if(mPreviewHandle.get())
{
mPreviewHandle.get()->die();
}
}
BOOL LLTwitterPhotoPanel::postBuild()
{
setVisibleCallback(boost::bind(&LLTwitterPhotoPanel::onVisibilityChange, this, _2));
mSnapshotPanel = getChild<LLUICtrl>("snapshot_panel");
mResolutionComboBox = getChild<LLUICtrl>("resolution_combobox");
mResolutionComboBox->setValue("[i800,i600]"); // hardcoded defaults ftw!
mResolutionComboBox->setCommitCallback(boost::bind(&LLTwitterPhotoPanel::updateResolution, this, TRUE));
mFilterComboBox = getChild<LLUICtrl>("filters_combobox");
mFilterComboBox->setCommitCallback(boost::bind(&LLTwitterPhotoPanel::updateResolution, this, TRUE));
mRefreshBtn = getChild<LLUICtrl>("new_snapshot_btn");
mBtnPreview = getChild<LLButton>("big_preview_btn");
mWorkingLabel = getChild<LLUICtrl>("working_lbl");
mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder");
mStatusCounterLabel = getChild<LLUICtrl>("status_counter_label");
mStatusTextBox = getChild<LLUICtrl>("photo_status");
mStatusTextBox->setValue(DEFAULT_STATUS_TEXT);
mLocationCheckbox = getChild<LLUICtrl>("add_location_cb");
mLocationCheckbox->setCommitCallback(boost::bind(&LLTwitterPhotoPanel::onAddLocationToggled, this));
mPhotoCheckbox = getChild<LLUICtrl>("add_photo_cb");
mPhotoCheckbox->setCommitCallback(boost::bind(&LLTwitterPhotoPanel::onAddPhotoToggled, this));
mPostButton = getChild<LLUICtrl>("post_photo_btn");
mCancelButton = getChild<LLUICtrl>("cancel_photo_btn");
mBigPreviewFloater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
// Update filter list
std::vector<std::string> filter_list = LLImageFiltersManager::getInstance()->getFiltersList();
LLComboBox* filterbox = static_cast<LLComboBox *>(mFilterComboBox);
for (U32 i = 0; i < filter_list.size(); i++)
{
filterbox->add(filter_list[i]);
}
return LLPanel::postBuild();
}
// virtual
S32 LLTwitterPhotoPanel::notify(const LLSD& info)
{
if (info.has("snapshot-updating"))
{
// Disable the Post button and whatever else while the snapshot is not updated
// updateControls();
return 1;
}
if (info.has("snapshot-updated"))
{
// Enable the send/post/save buttons.
updateControls();
// The refresh button is initially hidden. We show it after the first update,
// i.e. after snapshot is taken
LLUICtrl * refresh_button = getRefreshBtn();
if (!refresh_button->getVisible())
{
refresh_button->setVisible(true);
}
return 1;
}
return 0;
}
void LLTwitterPhotoPanel::draw()
{
LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get());
// Enable interaction only if no transaction with the service is on-going (prevent duplicated posts)
bool no_ongoing_connection = !(LLTwitterConnect::instance().isTransactionOngoing());
bool photo_checked = mPhotoCheckbox->getValue().asBoolean();
mCancelButton->setEnabled(no_ongoing_connection);
mStatusTextBox->setEnabled(no_ongoing_connection);
mResolutionComboBox->setEnabled(no_ongoing_connection && photo_checked);
mFilterComboBox->setEnabled(no_ongoing_connection && photo_checked);
mRefreshBtn->setEnabled(no_ongoing_connection && photo_checked);
mBtnPreview->setEnabled(no_ongoing_connection);
mLocationCheckbox->setEnabled(no_ongoing_connection);
mPhotoCheckbox->setEnabled(no_ongoing_connection);
bool add_location = mLocationCheckbox->getValue().asBoolean();
bool add_photo = mPhotoCheckbox->getValue().asBoolean();
updateStatusTextLength(false);
// Reassign the preview floater if we have the focus and the preview exists
if (hasFocus() && isPreviewVisible())
{
attachPreview();
}
// Toggle the button state as appropriate
bool preview_active = (isPreviewVisible() && mBigPreviewFloater->isFloaterOwner(getParentByType<LLFloater>()));
mBtnPreview->setToggleState(preview_active);
// Display the preview if one is available
if (previewp && previewp->getThumbnailImage())
{
const LLRect& thumbnail_rect = mThumbnailPlaceholder->getRect();
const S32 thumbnail_w = previewp->getThumbnailWidth();
const S32 thumbnail_h = previewp->getThumbnailHeight();
// calc preview offset within the preview rect
const S32 local_offset_x = (thumbnail_rect.getWidth() - thumbnail_w) / 2 ;
const S32 local_offset_y = (thumbnail_rect.getHeight() - thumbnail_h) / 2 ;
// calc preview offset within the floater rect
// Hack : To get the full offset, we need to take into account each and every offset of each widgets up to the floater.
// This is almost as arbitrary as using a fixed offset so that's what we do here for the sake of simplicity.
// *TODO : Get the offset looking through the hierarchy of widgets, should be done in postBuild() so to avoid traversing the hierarchy each time.
S32 offset_x = thumbnail_rect.mLeft + local_offset_x - 1;
S32 offset_y = thumbnail_rect.mBottom + local_offset_y - 39;
mSnapshotPanel->localPointToOtherView(offset_x, offset_y, &offset_x, &offset_y, getParentByType<LLFloater>());
gGL.matrixMode(LLRender::MM_MODELVIEW);
// Apply floater transparency to the texture unless the floater is focused.
F32 alpha = (add_photo ? (getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency()) : 0.5f);
LLColor4 color = LLColor4::white;
gl_draw_scaled_image(offset_x, offset_y,
thumbnail_w, thumbnail_h,
previewp->getThumbnailImage(), color % alpha);
}
// Update the visibility of the working (computing preview) label
mWorkingLabel->setVisible(!(previewp && previewp->getSnapshotUpToDate()));
// Enable Post if we have a preview to send and no on going connection being processed
mPostButton->setEnabled(no_ongoing_connection && (previewp && previewp->getSnapshotUpToDate()) && (add_photo || add_location || !mStatusTextBox->getValue().asString().empty()));
// Draw the rest of the panel on top of it
LLPanel::draw();
}
LLSnapshotLivePreview* LLTwitterPhotoPanel::getPreviewView()
{
LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)mPreviewHandle.get();
return previewp;
}
void LLTwitterPhotoPanel::onVisibilityChange(BOOL visible)
{
if (visible)
{
if (mPreviewHandle.get())
{
LLSnapshotLivePreview* preview = getPreviewView();
if(preview)
{
lldebugs << "opened, updating snapshot" << llendl;
preview->updateSnapshot(TRUE);
}
}
else
{
LLRect full_screen_rect = getRootView()->getRect();
LLSnapshotLivePreview::Params p;
p.rect(full_screen_rect);
LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p);
mPreviewHandle = previewp->getHandle();
previewp->setContainer(this);
previewp->setSnapshotType(previewp->SNAPSHOT_WEB);
previewp->setSnapshotFormat(LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG);
previewp->setThumbnailSubsampled(TRUE); // We want the preview to reflect the *saved* image
previewp->setAllowRenderUI(FALSE); // We do not want the rendered UI in our snapshots
previewp->setAllowFullScreenPreview(FALSE); // No full screen preview in SL Share mode
previewp->setThumbnailPlaceholderRect(mThumbnailPlaceholder->getRect());
updateControls();
}
}
}
void LLTwitterPhotoPanel::onAddLocationToggled()
{
bool add_location = mLocationCheckbox->getValue().asBoolean();
updateStatusTextLength(!add_location);
}
void LLTwitterPhotoPanel::onAddPhotoToggled()
{
bool add_photo = mPhotoCheckbox->getValue().asBoolean();
updateStatusTextLength(!add_photo);
}
void LLTwitterPhotoPanel::onClickNewSnapshot()
{
LLSnapshotLivePreview* previewp = getPreviewView();
if (previewp)
{
previewp->updateSnapshot(TRUE);
}
}
void LLTwitterPhotoPanel::onClickBigPreview()
{
// Toggle the preview
if (isPreviewVisible())
{
LLFloaterReg::hideInstance("big_preview");
}
else
{
attachPreview();
LLFloaterReg::showInstance("big_preview");
}
}
bool LLTwitterPhotoPanel::isPreviewVisible()
{
return (mBigPreviewFloater && mBigPreviewFloater->getVisible());
}
void LLTwitterPhotoPanel::attachPreview()
{
if (mBigPreviewFloater)
{
LLSnapshotLivePreview* previewp = getPreviewView();
mBigPreviewFloater->setPreview(previewp);
mBigPreviewFloater->setFloaterOwner(getParentByType<LLFloater>());
}
}
void LLTwitterPhotoPanel::onSend()
{
LLEventPumps::instance().obtain("TwitterConnectState").stopListening("LLTwitterPhotoPanel"); // just in case it is already listening
LLEventPumps::instance().obtain("TwitterConnectState").listen("LLTwitterPhotoPanel", boost::bind(&LLTwitterPhotoPanel::onTwitterConnectStateChange, this, _1));
// Connect to Twitter if necessary and then post
if (LLTwitterConnect::instance().isConnected())
{
sendPhoto();
}
else
{
LLTwitterConnect::instance().checkConnectionToTwitter(true);
}
}
bool LLTwitterPhotoPanel::onTwitterConnectStateChange(const LLSD& data)
{
switch (data.get("enum").asInteger())
{
case LLTwitterConnect::TWITTER_CONNECTED:
sendPhoto();
break;
case LLTwitterConnect::TWITTER_POSTED:
LLEventPumps::instance().obtain("TwitterConnectState").stopListening("LLTwitterPhotoPanel");
clearAndClose();
break;
}
return false;
}
void LLTwitterPhotoPanel::sendPhoto()
{
// Get the status text
std::string status = mStatusTextBox->getValue().asString();
// Add the location if required
bool add_location = mLocationCheckbox->getValue().asBoolean();
if (add_location)
{
// Get the SLURL for the location
LLSLURL slurl;
LLAgentUI::buildSLURL(slurl);
std::string slurl_string = slurl.getSLURLString();
// Use a valid http:// URL if the scheme is secondlife://
LLURI slurl_uri(slurl_string);
if (slurl_uri.scheme() == LLSLURL::SLURL_SECONDLIFE_SCHEME)
{
slurl_string = DEFAULT_PHOTO_LOCATION_URL;
}
// Add query parameters so Google Analytics can track incoming clicks!
slurl_string += DEFAULT_PHOTO_QUERY_PARAMETERS;
// Add it to the status (pretty crude, but we don't have a better option with photos)
if (status.empty())
status = slurl_string;
else
status = status + " " + slurl_string;
}
// Add the photo if required
bool add_photo = mPhotoCheckbox->getValue().asBoolean();
if (add_photo)
{
// Get the image
LLSnapshotLivePreview* previewp = getPreviewView();
// Post to Twitter
LLTwitterConnect::instance().uploadPhoto(previewp->getFormattedImage(), status);
}
else
{
// Just post the status to Twitter
LLTwitterConnect::instance().updateStatus(status);
}
updateControls();
}
void LLTwitterPhotoPanel::clearAndClose()
{
mStatusTextBox->setValue(DEFAULT_STATUS_TEXT);
LLFloater* floater = getParentByType<LLFloater>();
if (floater)
{
floater->closeFloater();
if (mBigPreviewFloater)
{
mBigPreviewFloater->closeOnFloaterOwnerClosing(floater);
}
}
}
void LLTwitterPhotoPanel::updateStatusTextLength(BOOL restore_old_status_text)
{
bool add_location = mLocationCheckbox->getValue().asBoolean();
bool add_photo = mPhotoCheckbox->getValue().asBoolean();
// Restrict the status text length to Twitter's character limit
LLTextEditor* status_text_box = dynamic_cast<LLTextEditor*>(mStatusTextBox);
if (status_text_box)
{
int max_status_length = 140 - (add_location ? 40 : 0) - (add_photo ? 40 : 0);
status_text_box->setMaxTextLength(max_status_length);
if (restore_old_status_text)
{
if (mOldStatusText.length() > status_text_box->getText().length() && status_text_box->getText() == mOldStatusText.substr(0, status_text_box->getText().length()))
{
status_text_box->setText(mOldStatusText);
}
if (mOldStatusText.length() <= max_status_length)
{
mOldStatusText = "";
}
}
if (status_text_box->getText().length() > max_status_length)
{
if (mOldStatusText.length() < status_text_box->getText().length() || status_text_box->getText() != mOldStatusText.substr(0, status_text_box->getText().length()))
{
mOldStatusText = status_text_box->getText();
}
status_text_box->setText(mOldStatusText.substr(0, max_status_length));
}
// Update the status character counter
int characters_remaining = max_status_length - status_text_box->getText().length();
mStatusCounterLabel->setValue(characters_remaining);
}
}
void LLTwitterPhotoPanel::updateControls()
{
LLSnapshotLivePreview* previewp = getPreviewView();
BOOL got_snap = previewp && previewp->getSnapshotUpToDate();
// *TODO: Separate maximum size for Web images from postcards
lldebugs << "Is snapshot up-to-date? " << got_snap << llendl;
updateResolution(FALSE);
}
void LLTwitterPhotoPanel::updateResolution(BOOL do_update)
{
LLComboBox* combobox = static_cast<LLComboBox *>(mResolutionComboBox);
LLComboBox* filterbox = static_cast<LLComboBox *>(mFilterComboBox);
std::string sdstring = combobox->getSelectedValue();
LLSD sdres;
std::stringstream sstream(sdstring);
LLSDSerialize::fromNotation(sdres, sstream, sdstring.size());
S32 width = sdres[0];
S32 height = sdres[1];
// Note : index 0 of the filter drop down is assumed to be "No filter" in whichever locale
std::string filter_name = (filterbox->getCurrentIndex() ? filterbox->getSimple() : "");
LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get());
if (previewp && combobox->getCurrentIndex() >= 0)
{
S32 original_width = 0 , original_height = 0 ;
previewp->getSize(original_width, original_height) ;
if (width == 0 || height == 0)
{
// take resolution from current window size
lldebugs << "Setting preview res from window: " << gViewerWindow->getWindowWidthRaw() << "x" << gViewerWindow->getWindowHeightRaw() << llendl;
previewp->setSize(gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw());
}
else
{
// use the resolution from the selected pre-canned drop-down choice
lldebugs << "Setting preview res selected from combo: " << width << "x" << height << llendl;
previewp->setSize(width, height);
}
checkAspectRatio(width);
previewp->getSize(width, height);
if (original_width != width || original_height != height)
{
previewp->setSize(width, height);
if (do_update)
{
previewp->updateSnapshot(TRUE);
updateControls();
}
}
// Get the old filter, compare to the current one "filter_name" and set if changed
std::string original_filter = previewp->getFilter();
if (original_filter != filter_name)
{
previewp->setFilter(filter_name);
if (do_update)
{
previewp->updateSnapshot(FALSE, TRUE);
updateControls();
}
}
}
}
void LLTwitterPhotoPanel::checkAspectRatio(S32 index)
{
LLSnapshotLivePreview *previewp = getPreviewView() ;
BOOL keep_aspect = FALSE;
if (0 == index) // current window size
{
keep_aspect = TRUE;
}
else // predefined resolution
{
keep_aspect = FALSE;
}
if (previewp)
{
previewp->mKeepAspectRatio = keep_aspect;
}
}
LLUICtrl* LLTwitterPhotoPanel::getRefreshBtn()
{
return mRefreshBtn;
}
///////////////////////////
//LLTwitterAccountPanel//////
///////////////////////////
LLTwitterAccountPanel::LLTwitterAccountPanel() :
mAccountCaptionLabel(NULL),
mAccountNameLabel(NULL),
mPanelButtons(NULL),
mConnectButton(NULL),
mDisconnectButton(NULL)
{
mCommitCallbackRegistrar.add("SocialSharing.Connect", boost::bind(&LLTwitterAccountPanel::onConnect, this));
mCommitCallbackRegistrar.add("SocialSharing.Disconnect", boost::bind(&LLTwitterAccountPanel::onDisconnect, this));
setVisibleCallback(boost::bind(&LLTwitterAccountPanel::onVisibilityChange, this, _2));
}
BOOL LLTwitterAccountPanel::postBuild()
{
mAccountCaptionLabel = getChild<LLTextBox>("account_caption_label");
mAccountNameLabel = getChild<LLTextBox>("account_name_label");
mPanelButtons = getChild<LLUICtrl>("panel_buttons");
mConnectButton = getChild<LLUICtrl>("connect_btn");
mDisconnectButton = getChild<LLUICtrl>("disconnect_btn");
return LLPanel::postBuild();
}
void LLTwitterAccountPanel::draw()
{
LLTwitterConnect::EConnectionState connection_state = LLTwitterConnect::instance().getConnectionState();
//Disable the 'disconnect' button and the 'use another account' button when disconnecting in progress
bool disconnecting = connection_state == LLTwitterConnect::TWITTER_DISCONNECTING;
mDisconnectButton->setEnabled(!disconnecting);
//Disable the 'connect' button when a connection is in progress
bool connecting = connection_state == LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS;
mConnectButton->setEnabled(!connecting);
LLPanel::draw();
}
void LLTwitterAccountPanel::onVisibilityChange(BOOL visible)
{
if(visible)
{
LLEventPumps::instance().obtain("TwitterConnectState").stopListening("LLTwitterAccountPanel");
LLEventPumps::instance().obtain("TwitterConnectState").listen("LLTwitterAccountPanel", boost::bind(&LLTwitterAccountPanel::onTwitterConnectStateChange, this, _1));
LLEventPumps::instance().obtain("TwitterConnectInfo").stopListening("LLTwitterAccountPanel");
LLEventPumps::instance().obtain("TwitterConnectInfo").listen("LLTwitterAccountPanel", boost::bind(&LLTwitterAccountPanel::onTwitterConnectInfoChange, this));
//Connected
if(LLTwitterConnect::instance().isConnected())
{
showConnectedLayout();
}
//Check if connected (show disconnected layout in meantime)
else
{
showDisconnectedLayout();
}
if ((LLTwitterConnect::instance().getConnectionState() == LLTwitterConnect::TWITTER_NOT_CONNECTED) ||
(LLTwitterConnect::instance().getConnectionState() == LLTwitterConnect::TWITTER_CONNECTION_FAILED))
{
LLTwitterConnect::instance().checkConnectionToTwitter();
}
}
else
{
LLEventPumps::instance().obtain("TwitterConnectState").stopListening("LLTwitterAccountPanel");
LLEventPumps::instance().obtain("TwitterConnectInfo").stopListening("LLTwitterAccountPanel");
}
}
bool LLTwitterAccountPanel::onTwitterConnectStateChange(const LLSD& data)
{
if(LLTwitterConnect::instance().isConnected())
{
//In process of disconnecting so leave the layout as is
if(data.get("enum").asInteger() != LLTwitterConnect::TWITTER_DISCONNECTING)
{
showConnectedLayout();
}
}
else
{
showDisconnectedLayout();
}
return false;
}
bool LLTwitterAccountPanel::onTwitterConnectInfoChange()
{
LLSD info = LLTwitterConnect::instance().getInfo();
std::string clickable_name;
//Strings of format [http://www.somewebsite.com Click Me] become clickable text
if(info.has("link") && info.has("name"))
{
clickable_name = "[" + info["link"].asString() + " " + info["name"].asString() + "]";
}
mAccountNameLabel->setText(clickable_name);
return false;
}
void LLTwitterAccountPanel::showConnectButton()
{
if(!mConnectButton->getVisible())
{
mConnectButton->setVisible(TRUE);
mDisconnectButton->setVisible(FALSE);
}
}
void LLTwitterAccountPanel::hideConnectButton()
{
if(mConnectButton->getVisible())
{
mConnectButton->setVisible(FALSE);
mDisconnectButton->setVisible(TRUE);
}
}
void LLTwitterAccountPanel::showDisconnectedLayout()
{
mAccountCaptionLabel->setText(getString("twitter_disconnected"));
mAccountNameLabel->setText(std::string(""));
showConnectButton();
}
void LLTwitterAccountPanel::showConnectedLayout()
{
LLTwitterConnect::instance().loadTwitterInfo();
mAccountCaptionLabel->setText(getString("twitter_connected"));
hideConnectButton();
}
void LLTwitterAccountPanel::onConnect()
{
LLTwitterConnect::instance().checkConnectionToTwitter(true);
//Clear only the twitter browser cookies so that the twitter login screen appears
LLViewerMedia::getCookieStore()->removeCookiesByDomain(".twitter.com");
}
void LLTwitterAccountPanel::onDisconnect()
{
LLTwitterConnect::instance().disconnectFromTwitter();
LLViewerMedia::getCookieStore()->removeCookiesByDomain(".twitter.com");
}
////////////////////////
//LLFloaterTwitter///////
////////////////////////
LLFloaterTwitter::LLFloaterTwitter(const LLSD& key) : LLFloater(key),
mTwitterPhotoPanel(NULL),
mStatusErrorText(NULL),
mStatusLoadingText(NULL),
mStatusLoadingIndicator(NULL)
{
mCommitCallbackRegistrar.add("SocialSharing.Cancel", boost::bind(&LLFloaterTwitter::onCancel, this));
}
void LLFloaterTwitter::onClose(bool app_quitting)
{
LLFloaterBigPreview* big_preview_floater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
if (big_preview_floater)
{
big_preview_floater->closeOnFloaterOwnerClosing(this);
}
LLFloater::onClose(app_quitting);
}
void LLFloaterTwitter::onCancel()
{
LLFloaterBigPreview* big_preview_floater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
if (big_preview_floater)
{
big_preview_floater->closeOnFloaterOwnerClosing(this);
}
closeFloater();
}
BOOL LLFloaterTwitter::postBuild()
{
// Keep tab of the Photo Panel
mTwitterPhotoPanel = static_cast<LLTwitterPhotoPanel*>(getChild<LLUICtrl>("panel_twitter_photo"));
// Connection status widgets
mStatusErrorText = getChild<LLTextBox>("connection_error_text");
mStatusLoadingText = getChild<LLTextBox>("connection_loading_text");
mStatusLoadingIndicator = getChild<LLUICtrl>("connection_loading_indicator");
return LLFloater::postBuild();
}
void LLFloaterTwitter::showPhotoPanel()
{
LLTabContainer* parent = dynamic_cast<LLTabContainer*>(mTwitterPhotoPanel->getParent());
if (!parent)
{
llwarns << "Cannot find panel container" << llendl;
return;
}
parent->selectTabPanel(mTwitterPhotoPanel);
}
void LLFloaterTwitter::draw()
{
if (mStatusErrorText && mStatusLoadingText && mStatusLoadingIndicator)
{
mStatusErrorText->setVisible(false);
mStatusLoadingText->setVisible(false);
mStatusLoadingIndicator->setVisible(false);
LLTwitterConnect::EConnectionState connection_state = LLTwitterConnect::instance().getConnectionState();
std::string status_text;
switch (connection_state)
{
case LLTwitterConnect::TWITTER_NOT_CONNECTED:
// No status displayed when first opening the panel and no connection done
case LLTwitterConnect::TWITTER_CONNECTED:
// When successfully connected, no message is displayed
case LLTwitterConnect::TWITTER_POSTED:
// No success message to show since we actually close the floater after successful posting completion
break;
case LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS:
// Connection loading indicator
mStatusLoadingText->setVisible(true);
status_text = LLTrans::getString("SocialTwitterConnecting");
mStatusLoadingText->setValue(status_text);
mStatusLoadingIndicator->setVisible(true);
break;
case LLTwitterConnect::TWITTER_POSTING:
// Posting indicator
mStatusLoadingText->setVisible(true);
status_text = LLTrans::getString("SocialTwitterPosting");
mStatusLoadingText->setValue(status_text);
mStatusLoadingIndicator->setVisible(true);
break;
case LLTwitterConnect::TWITTER_CONNECTION_FAILED:
// Error connecting to the service
mStatusErrorText->setVisible(true);
status_text = LLTrans::getString("SocialTwitterErrorConnecting");
mStatusErrorText->setValue(status_text);
break;
case LLTwitterConnect::TWITTER_POST_FAILED:
// Error posting to the service
mStatusErrorText->setVisible(true);
status_text = LLTrans::getString("SocialTwitterErrorPosting");
mStatusErrorText->setValue(status_text);
break;
case LLTwitterConnect::TWITTER_DISCONNECTING:
// Disconnecting loading indicator
mStatusLoadingText->setVisible(true);
status_text = LLTrans::getString("SocialTwitterDisconnecting");
mStatusLoadingText->setValue(status_text);
mStatusLoadingIndicator->setVisible(true);
break;
case LLTwitterConnect::TWITTER_DISCONNECT_FAILED:
// Error disconnecting from the service
mStatusErrorText->setVisible(true);
status_text = LLTrans::getString("SocialTwitterErrorDisconnecting");
mStatusErrorText->setValue(status_text);
break;
}
}
LLFloater::draw();
}

View File

@ -0,0 +1,139 @@
/**
* @file llfloatertwitter.h
* @brief Header file for llfloatertwitter
* @author cho@lindenlab.com
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, 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_LLFLOATERTWITTER_H
#define LL_LLFLOATERTWITTER_H
#include "llfloater.h"
#include "lltextbox.h"
#include "llviewertexture.h"
class LLIconCtrl;
class LLCheckBoxCtrl;
class LLSnapshotLivePreview;
class LLFloaterBigPreview;
class LLTwitterPhotoPanel : public LLPanel
{
public:
LLTwitterPhotoPanel();
~LLTwitterPhotoPanel();
BOOL postBuild();
void draw();
LLSnapshotLivePreview* getPreviewView();
void onVisibilityChange(BOOL new_visibility);
void onAddLocationToggled();
void onAddPhotoToggled();
void onClickBigPreview();
void onClickNewSnapshot();
void onSend();
S32 notify(const LLSD& info);
bool onTwitterConnectStateChange(const LLSD& data);
void sendPhoto();
void clearAndClose();
void updateStatusTextLength(BOOL restore_old_status_text);
void updateControls();
void updateResolution(BOOL do_update);
void checkAspectRatio(S32 index);
LLUICtrl* getRefreshBtn();
private:
bool isPreviewVisible();
void attachPreview();
LLHandle<LLView> mPreviewHandle;
LLUICtrl * mSnapshotPanel;
LLUICtrl * mResolutionComboBox;
LLUICtrl * mFilterComboBox;
LLUICtrl * mRefreshBtn;
LLUICtrl * mWorkingLabel;
LLUICtrl * mThumbnailPlaceholder;
LLUICtrl * mStatusCounterLabel;
LLUICtrl * mStatusTextBox;
LLUICtrl * mLocationCheckbox;
LLUICtrl * mPhotoCheckbox;
LLUICtrl * mPostButton;
LLUICtrl * mCancelButton;
LLButton * mBtnPreview;
LLFloaterBigPreview * mBigPreviewFloater;
std::string mOldStatusText;
};
class LLTwitterAccountPanel : public LLPanel
{
public:
LLTwitterAccountPanel();
BOOL postBuild();
void draw();
private:
void onVisibilityChange(BOOL new_visibility);
bool onTwitterConnectStateChange(const LLSD& data);
bool onTwitterConnectInfoChange();
void onConnect();
void onUseAnotherAccount();
void onDisconnect();
void showConnectButton();
void hideConnectButton();
void showDisconnectedLayout();
void showConnectedLayout();
LLTextBox * mAccountCaptionLabel;
LLTextBox * mAccountNameLabel;
LLUICtrl * mPanelButtons;
LLUICtrl * mConnectButton;
LLUICtrl * mDisconnectButton;
};
class LLFloaterTwitter : public LLFloater
{
public:
LLFloaterTwitter(const LLSD& key);
BOOL postBuild();
void draw();
void onClose(bool app_quitting);
void onCancel();
void showPhotoPanel();
private:
LLTwitterPhotoPanel* mTwitterPhotoPanel;
LLTextBox* mStatusErrorText;
LLTextBox* mStatusLoadingText;
LLUICtrl* mStatusLoadingIndicator;
};
#endif // LL_LLFLOATERTWITTER_H

View File

@ -1050,12 +1050,11 @@ void LLFloaterUIPreview::onClickEditFloater()
// Respond to button click to browse for an executable with which to edit XML files
void LLFloaterUIPreview::onClickBrowseForEditor()
{
// create load dialog box
LLFilePicker::ELoadFilter type = (LLFilePicker::ELoadFilter)((intptr_t)((void*)LLFilePicker::FFLOAD_ALL)); // nothing for *.exe so just use all
// Let the user choose an executable through the file picker dialog box
LLFilePicker& picker = LLFilePicker::instance();
if (!picker.getOpenFile(type)) // user cancelled -- do nothing
if (!picker.getOpenFile(LLFilePicker::FFLOAD_EXE))
{
return;
return; // user cancelled -- do nothing
}
// put the selected path into text field

View File

@ -31,6 +31,8 @@
#include "llfloaterreg.h"
#include "llhttpconstants.h"
#include "llfacebookconnect.h"
#include "llflickrconnect.h"
#include "lltwitterconnect.h"
#include "lllayoutstack.h"
#include "llpluginclassmedia.h"
#include "llprogressbar.h"
@ -52,7 +54,8 @@ LLFloaterWebContent::_Params::_Params()
allow_back_forward_navigation("allow_back_forward_navigation", true),
preferred_media_size("preferred_media_size"),
trusted_content("trusted_content", false),
show_page_title("show_page_title", true)
show_page_title("show_page_title", true),
clean_browser("clean_browser", false)
{}
LLFloaterWebContent::LLFloaterWebContent( const Params& params )
@ -249,7 +252,7 @@ void LLFloaterWebContent::open_media(const Params& p)
LLViewerMedia::proxyWindowOpened(p.target(), p.id());
mWebBrowser->setHomePageUrl(p.url, HTTP_CONTENT_TEXT_HTML);
mWebBrowser->setTarget(p.target);
mWebBrowser->navigateTo(p.url, HTTP_CONTENT_TEXT_HTML);
mWebBrowser->navigateTo(p.url, HTTP_CONTENT_TEXT_HTML, p.clean_browser);
set_current_url(p.url);
@ -260,11 +263,6 @@ void LLFloaterWebContent::open_media(const Params& p)
getChildView("address")->setEnabled(address_entry_enabled);
getChildView("popexternal")->setEnabled(address_entry_enabled);
if (!address_entry_enabled)
{
mWebBrowser->setFocus(TRUE);
}
if (!p.show_chrome)
{
setResizeLimits(100, 100);
@ -318,7 +316,24 @@ void LLFloaterWebContent::onClose(bool app_quitting)
LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_FAILED);
}
}
// Same with Flickr
LLFloater* flickr_web = LLFloaterReg::getInstance("flickr_web");
if (flickr_web == this)
{
if (!LLFlickrConnect::instance().isConnected())
{
LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_FAILED);
}
}
// And Twitter
LLFloater* twitter_web = LLFloaterReg::getInstance("twitter_web");
if (twitter_web == this)
{
if (!LLTwitterConnect::instance().isConnected())
{
LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_FAILED);
}
}
LLViewerMedia::proxyWindowClosed(mUUID);
destroy();
}
@ -457,9 +472,6 @@ void LLFloaterWebContent::set_current_url(const std::string& url)
mAddressCombo->remove(mCurrentURL);
mAddressCombo->add(mDisplayURL);
mAddressCombo->selectByValue(mDisplayURL);
// Set the focus back to the web page. When setting the url, there's no point to leave the focus anywhere else.
mWebBrowser->setFocus(TRUE);
}
}

View File

@ -57,7 +57,8 @@ public:
allow_address_entry,
allow_back_forward_navigation,
trusted_content,
show_page_title;
show_page_title,
clean_browser;
Optional<LLRect> preferred_media_size;
_Params();

View File

@ -0,0 +1,115 @@
/**
* @file llimagefiltersmanager.cpp
* @brief Load image filters list and retrieve their path. Mostly used for Flickr UI at the moment.
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2014, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "llimagefiltersmanager.h"
#include "lldiriterator.h"
#include "lltrans.h"
std::string get_sys_dir()
{
return gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "filters", "");
}
//---------------------------------------------------------------------------
// LLImageFiltersManager
//---------------------------------------------------------------------------
LLImageFiltersManager::LLImageFiltersManager()
{
}
LLImageFiltersManager::~LLImageFiltersManager()
{
}
// virtual static
void LLImageFiltersManager::initSingleton()
{
loadAllFilters();
}
void LLImageFiltersManager::loadAllFilters()
{
// Load system (coming out of the box) filters
loadFiltersFromDir(get_sys_dir());
}
void LLImageFiltersManager::loadFiltersFromDir(const std::string& dir)
{
mFiltersList.clear();
LLDirIterator dir_iter(dir, "*.xml");
while (1)
{
std::string file_name;
if (!dir_iter.next(file_name))
{
break; // no more files
}
// Get the ".xml" out of the file name to get the filter name. That's the one known in strings.xml
std::string filter_name_untranslated = file_name.substr(0,file_name.length()-4);
// Get the localized name for the filter
std::string filter_name_translated;
bool translated = LLTrans::findString(filter_name_translated, filter_name_untranslated);
std::string filter_name = (translated ? filter_name_translated: filter_name_untranslated);
// Store the filter in the list with its associated file name
mFiltersList[filter_name] = file_name;
}
}
// Note : That method is a bit heavy handed but the list of filters is always small (10 or so)
// and that method is typically called only once when building UI widgets.
const std::vector<std::string> LLImageFiltersManager::getFiltersList() const
{
std::vector<std::string> filter_list;
for (std::map<std::string,std::string>::const_iterator it = mFiltersList.begin(); it != mFiltersList.end(); ++it)
{
filter_list.push_back(it->first);
}
return filter_list;
}
std::string LLImageFiltersManager::getFilterPath(const std::string& filter_name)
{
std::string path = "";
std::map<std::string,std::string>::const_iterator it = mFiltersList.find(filter_name);
if (it != mFiltersList.end())
{
// Get the file name for that filter and build the complete path
std::string file = it->second;
std::string dir = get_sys_dir();
path = gDirUtilp->add(dir, file);
}
return path;
}
//============================================================================

View File

@ -0,0 +1,55 @@
/**
* @file llimagefiltersmanager.h
* @brief Load image filters list and retrieve their path. Mostly used for Flickr UI at the moment.
*
* $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2014, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLIMAGEFILTERSMANAGER_H
#define LL_LLIMAGEFILTERSMANAGER_H
#include "llsingleton.h"
//============================================================================
// LLImageFiltersManager class
class LLImageFiltersManager : public LLSingleton<LLImageFiltersManager>
{
LOG_CLASS(LLImageFiltersManager);
public:
const std::vector<std::string> getFiltersList() const;
std::string getFilterPath(const std::string& filter_name);
private:
void loadAllFilters();
void loadFiltersFromDir(const std::string& dir);
friend class LLSingleton<LLImageFiltersManager>;
/*virtual*/ void initSingleton();
LLImageFiltersManager();
~LLImageFiltersManager();
// List of filters : first is the user friendly localized name, second is the xml file name
std::map<std::string,std::string> mFiltersList;
};
#endif // LL_LLIMAGEFILTERSMANAGER_H

View File

@ -561,7 +561,7 @@ void LLMediaCtrl::clearCache()
////////////////////////////////////////////////////////////////////////////////
//
void LLMediaCtrl::navigateTo( std::string url_in, std::string mime_type)
void LLMediaCtrl::navigateTo( std::string url_in, std::string mime_type, bool clean_browser)
{
// <AW>
// don't browse to slurls like "secondlife://" or "hop://"
@ -580,7 +580,7 @@ void LLMediaCtrl::navigateTo( std::string url_in, std::string mime_type)
{
mCurrentNavUrl = url_in;
mMediaSource->setSize(mTextureWidth, mTextureHeight);
mMediaSource->navigateTo(url_in, mime_type, mime_type.empty());
mMediaSource->navigateTo(url_in, mime_type, mime_type.empty(), false, clean_browser);
}
}

View File

@ -95,7 +95,7 @@ public:
virtual BOOL handleToolTip(S32 x, S32 y, MASK mask);
// navigation
void navigateTo( std::string url_in, std::string mime_type = "");
void navigateTo( std::string url_in, std::string mime_type = "", bool clean_browser = false);
void navigateBack();
void navigateHome();
void navigateForward();

View File

@ -521,7 +521,7 @@ public:
LLPanelPeople::LLPanelPeople()
: LLPanel(),
mTryToConnectToFbc(true),
mTryToConnectToFacebook(true),
mTabContainer(NULL),
mOnlineFriendList(NULL),
mAllFriendList(NULL),
@ -963,10 +963,10 @@ void LLPanelPeople::updateFacebookList(bool visible)
{
LLFacebookConnect::instance().loadFacebookFriends();
}
else if(mTryToConnectToFbc)
else if(mTryToConnectToFacebook)
{
LLFacebookConnect::instance().checkConnectionToFacebook();
mTryToConnectToFbc = false;
mTryToConnectToFacebook = false;
}
updateSuggestedFriendList();

View File

@ -67,7 +67,7 @@ public:
// when voice is available
/*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal);
bool mTryToConnectToFbc;
bool mTryToConnectToFacebook;
// internals
class Updater;

View File

@ -31,6 +31,10 @@
#include "llsidetraypanelcontainer.h"
#include "llfloatersnapshot.h" // FIXME: create a snapshot model
#include "llfloaterreg.h"
#include "llfloaterfacebook.h"
#include "llfloaterflickr.h"
#include "llfloatertwitter.h"
/**
* Provides several ways to save a snapshot.
@ -44,6 +48,7 @@ class LLPanelSnapshotOptions
public:
LLPanelSnapshotOptions();
~LLPanelSnapshotOptions();
/*virtual*/ BOOL postBuild();
/*virtual*/ void onOpen(const LLSD& key);
/*virtual*/ void onEconomyDataChange() { updateUploadCost(); }
@ -54,6 +59,9 @@ private:
void onSaveToEmail();
void onSaveToInventory();
void onSaveToComputer();
void onSendToFacebook();
void onSendToTwitter();
void onSendToFlickr();
};
static LLPanelInjector<LLPanelSnapshotOptions> panel_class("llpanelsnapshotoptions");
@ -73,6 +81,19 @@ LLPanelSnapshotOptions::~LLPanelSnapshotOptions()
LLGlobalEconomy::Singleton::getInstance()->removeObserver(this);
}
// virtual
BOOL LLPanelSnapshotOptions::postBuild()
{
LLTextBox* sendToFacebookTextBox = getChild<LLTextBox>("send_to_facebook_textbox");
sendToFacebookTextBox->setURLClickedCallback(boost::bind(&LLPanelSnapshotOptions::onSendToFacebook, this));
LLTextBox* sendToTwitterTextBox = getChild<LLTextBox>("send_to_twitter_textbox");
sendToTwitterTextBox->setURLClickedCallback(boost::bind(&LLPanelSnapshotOptions::onSendToTwitter, this));
LLTextBox* sendToFlickrTextBox = getChild<LLTextBox>("send_to_flickr_textbox");
sendToFlickrTextBox->setURLClickedCallback(boost::bind(&LLPanelSnapshotOptions::onSendToFlickr, this));
return LLPanel::postBuild();
}
// virtual
void LLPanelSnapshotOptions::onOpen(const LLSD& key)
{
@ -118,3 +139,39 @@ void LLPanelSnapshotOptions::onSaveToComputer()
{
openPanel("panel_snapshot_local");
}
void LLPanelSnapshotOptions::onSendToFacebook()
{
LLFloaterReg::hideInstance("snapshot");
LLFloaterFacebook* facebook_floater = dynamic_cast<LLFloaterFacebook*>(LLFloaterReg::getInstance("facebook"));
if (facebook_floater)
{
facebook_floater->showPhotoPanel();
}
LLFloaterReg::showInstance("facebook");
}
void LLPanelSnapshotOptions::onSendToTwitter()
{
LLFloaterReg::hideInstance("snapshot");
LLFloaterTwitter* twitter_floater = dynamic_cast<LLFloaterTwitter*>(LLFloaterReg::getInstance("twitter"));
if (twitter_floater)
{
twitter_floater->showPhotoPanel();
}
LLFloaterReg::showInstance("twitter");
}
void LLPanelSnapshotOptions::onSendToFlickr()
{
LLFloaterReg::hideInstance("snapshot");
LLFloaterFlickr* flickr_floater = dynamic_cast<LLFloaterFlickr*>(LLFloaterReg::getInstance("flickr"));
if (flickr_floater)
{
flickr_floater->showPhotoPanel();
}
LLFloaterReg::showInstance("flickr");
}

View File

@ -34,7 +34,11 @@
#include "lleconomy.h"
#include "llfloaterperms.h"
#include "llfloaterreg.h"
#include "llfloatersocial.h"
#include "llfloaterfacebook.h"
#include "llfloaterflickr.h"
#include "llfloatertwitter.h"
#include "llimagefilter.h"
#include "llimagefiltersmanager.h"
#include "llimagebmp.h"
#include "llimagej2c.h"
#include "llimagejpeg.h"
@ -70,9 +74,11 @@ LLSnapshotLivePreview::LLSnapshotLivePreview (const LLSnapshotLivePreview::Param
mColor(1.f, 0.f, 0.f, 0.5f),
mCurImageIndex(0),
mPreviewImage(NULL),
mThumbnailImage(NULL) ,
mThumbnailImage(NULL) ,
mBigThumbnailImage(NULL) ,
mThumbnailWidth(0),
mThumbnailHeight(0),
mThumbnailSubsampled(FALSE),
mPreviewImageEncoded(NULL),
mFormattedImage(NULL),
mShineCountdown(0),
@ -86,7 +92,11 @@ LLSnapshotLivePreview::LLSnapshotLivePreview (const LLSnapshotLivePreview::Param
mCameraPos(LLViewerCamera::getInstance()->getOrigin()),
mCameraRot(LLViewerCamera::getInstance()->getQuaternion()),
mSnapshotActive(FALSE),
mSnapshotBufferType(LLViewerWindow::SNAPSHOT_TYPE_COLOR)
mSnapshotBufferType(LLViewerWindow::SNAPSHOT_TYPE_COLOR),
mFilterName(""),
mAllowRenderUI(TRUE),
mAllowFullScreenPreview(TRUE),
mViewContainer(NULL)
{
setSnapshotQuality(gSavedSettings.getS32("SnapshotQuality"));
mSnapshotDelayTimer.setTimerExpirySec(0.0f);
@ -105,6 +115,7 @@ LLSnapshotLivePreview::LLSnapshotLivePreview (const LLSnapshotLivePreview::Param
mKeepAspectRatio = gSavedSettings.getBOOL("KeepAspectForSnapshot") ;
mThumbnailUpdateLock = FALSE ;
mThumbnailUpToDate = FALSE ;
mBigThumbnailUpToDate = FALSE ;
}
LLSnapshotLivePreview::~LLSnapshotLivePreview()
@ -120,14 +131,7 @@ LLSnapshotLivePreview::~LLSnapshotLivePreview()
void LLSnapshotLivePreview::setMaxImageSize(S32 size)
{
if(size < MAX_SNAPSHOT_IMAGE_SIZE)
{
mMaxImageSize = size;
}
else
{
mMaxImageSize = MAX_SNAPSHOT_IMAGE_SIZE ;
}
mMaxImageSize = llmin(size,(S32)(MAX_SNAPSHOT_IMAGE_SIZE));
}
LLViewerTexture* LLSnapshotLivePreview::getCurrentImage()
@ -135,97 +139,92 @@ LLViewerTexture* LLSnapshotLivePreview::getCurrentImage()
return mViewerImage[mCurImageIndex];
}
F32 LLSnapshotLivePreview::getAspect()
{
F32 image_aspect_ratio = ((F32)getWidth()) / ((F32)getHeight());
F32 window_aspect_ratio = ((F32)getRect().getWidth()) / ((F32)getRect().getHeight());
if (!mKeepAspectRatio)//gSavedSettings.getBOOL("KeepAspectForSnapshot"))
{
return image_aspect_ratio;
}
else
{
return window_aspect_ratio;
}
}
F32 LLSnapshotLivePreview::getImageAspect()
{
if (!getCurrentImage())
{
return 0.f;
}
return getAspect() ;
// mKeepAspectRatio) == gSavedSettings.getBOOL("KeepAspectForSnapshot"))
return (mKeepAspectRatio ? ((F32)getRect().getWidth()) / ((F32)getRect().getHeight()) : ((F32)getWidth()) / ((F32)getHeight()));
}
void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail, F32 delay)
void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail, F32 delay)
{
// Invalidate current image.
LL_DEBUGS() << "updateSnapshot: mSnapshotUpToDate = " << getSnapshotUpToDate() << LL_ENDL;
if (getSnapshotUpToDate())
{
S32 old_image_index = mCurImageIndex;
mCurImageIndex = (mCurImageIndex + 1) % 2;
setSize(mWidth[old_image_index], mHeight[old_image_index]);
mFallAnimTimer.start();
}
mSnapshotUpToDate = FALSE;
// Update snapshot source rect depending on whether we keep the aspect ratio.
LLRect& rect = mImageRect[mCurImageIndex];
rect.set(0, getRect().getHeight(), getRect().getWidth(), 0);
F32 image_aspect_ratio = ((F32)getWidth()) / ((F32)getHeight());
F32 window_aspect_ratio = ((F32)getRect().getWidth()) / ((F32)getRect().getHeight());
if (mKeepAspectRatio)//gSavedSettings.getBOOL("KeepAspectForSnapshot"))
{
if (image_aspect_ratio > window_aspect_ratio)
{
// trim off top and bottom
S32 new_height = llround((F32)getRect().getWidth() / image_aspect_ratio);
rect.mBottom += (getRect().getHeight() - new_height) / 2;
rect.mTop -= (getRect().getHeight() - new_height) / 2;
}
else if (image_aspect_ratio < window_aspect_ratio)
{
// trim off left and right
S32 new_width = llround((F32)getRect().getHeight() * image_aspect_ratio);
rect.mLeft += (getRect().getWidth() - new_width) / 2;
rect.mRight -= (getRect().getWidth() - new_width) / 2;
}
}
// Stop shining animation.
mShineAnimTimer.stop();
lldebugs << "updateSnapshot: mSnapshotUpToDate = " << getSnapshotUpToDate() << llendl;
// Update snapshot if requested.
if (new_snapshot)
{
if (getSnapshotUpToDate())
{
S32 old_image_index = mCurImageIndex;
mCurImageIndex = (mCurImageIndex + 1) % 2;
setSize(mWidth[old_image_index], mHeight[old_image_index]);
mFallAnimTimer.start();
}
mSnapshotUpToDate = FALSE;
// Update snapshot source rect depending on whether we keep the aspect ratio.
LLRect& rect = mImageRect[mCurImageIndex];
rect.set(0, getRect().getHeight(), getRect().getWidth(), 0);
F32 image_aspect_ratio = ((F32)getWidth()) / ((F32)getHeight());
F32 window_aspect_ratio = ((F32)getRect().getWidth()) / ((F32)getRect().getHeight());
if (mKeepAspectRatio)//gSavedSettings.getBOOL("KeepAspectForSnapshot"))
{
if (image_aspect_ratio > window_aspect_ratio)
{
// trim off top and bottom
S32 new_height = llround((F32)getRect().getWidth() / image_aspect_ratio);
rect.mBottom += (getRect().getHeight() - new_height) / 2;
rect.mTop -= (getRect().getHeight() - new_height) / 2;
}
else if (image_aspect_ratio < window_aspect_ratio)
{
// trim off left and right
S32 new_width = llround((F32)getRect().getHeight() * image_aspect_ratio);
rect.mLeft += (getRect().getWidth() - new_width) / 2;
rect.mRight -= (getRect().getWidth() - new_width) / 2;
}
}
// Stop shining animation.
mShineAnimTimer.stop();
mSnapshotDelayTimer.start();
mSnapshotDelayTimer.setTimerExpirySec(delay);
LLFloaterSnapshot::preUpdate();
LLFloaterSocial::preUpdate();
// Tell the floater container that the snapshot is in the process of updating itself
if (mViewContainer)
{
mViewContainer->notify(LLSD().with("snapshot-updating", true));
}
}
// Update thumbnail if requested.
if(new_thumbnail)
if (new_thumbnail)
{
mThumbnailUpToDate = FALSE ;
mBigThumbnailUpToDate = FALSE;
}
}
void LLSnapshotLivePreview::setSnapshotQuality(S32 quality)
// Return true if the quality has been changed, false otherwise
bool LLSnapshotLivePreview::setSnapshotQuality(S32 quality, bool set_by_user)
{
llclamp(quality, 0, 100);
if (quality != mSnapshotQuality)
{
mSnapshotQuality = quality;
gSavedSettings.setS32("SnapshotQuality", quality);
mSnapshotUpToDate = FALSE;
if (set_by_user)
{
gSavedSettings.setS32("SnapshotQuality", quality);
}
mFormattedImage = NULL; // Invalidate the already formatted image if any
return true;
}
return false;
}
void LLSnapshotLivePreview::drawPreviewRect(S32 offset_x, S32 offset_y)
@ -459,68 +458,57 @@ void LLSnapshotLivePreview::reshape(S32 width, S32 height, BOOL called_from_pare
if (old_rect.getWidth() != width || old_rect.getHeight() != height)
{
LL_DEBUGS() << "window reshaped, updating thumbnail" << LL_ENDL;
updateSnapshot(FALSE, TRUE);
updateSnapshot(TRUE);
}
}
BOOL LLSnapshotLivePreview::setThumbnailImageSize()
{
if(getWidth() < 10 || getHeight() < 10)
if (getWidth() < 10 || getHeight() < 10)
{
return FALSE ;
}
S32 window_width = gViewerWindow->getWindowWidthRaw() ;
S32 window_height = gViewerWindow->getWindowHeightRaw() ;
S32 width = (mThumbnailSubsampled ? mPreviewImage->getWidth() : gViewerWindow->getWindowWidthRaw());
S32 height = (mThumbnailSubsampled ? mPreviewImage->getHeight() : gViewerWindow->getWindowHeightRaw()) ;
F32 window_aspect_ratio = ((F32)window_width) / ((F32)window_height);
F32 aspect_ratio = ((F32)width) / ((F32)height);
// UI size for thumbnail
// *FIXME: the rect does not change, so maybe there's no need to recalculate max w/h.
const LLRect& thumbnail_rect = mThumbnailPlaceholderRect;
S32 max_width = thumbnail_rect.getWidth();
S32 max_height = thumbnail_rect.getHeight();
S32 max_width = mThumbnailPlaceholderRect.getWidth();
S32 max_height = mThumbnailPlaceholderRect.getHeight();
if (window_aspect_ratio > (F32)max_width / max_height)
if (aspect_ratio > (F32)max_width / (F32)max_height)
{
// image too wide, shrink to width
mThumbnailWidth = max_width;
mThumbnailHeight = llround((F32)max_width / window_aspect_ratio);
mThumbnailHeight = llround((F32)max_width / aspect_ratio);
}
else
{
// image too tall, shrink to height
mThumbnailHeight = max_height;
mThumbnailWidth = llround((F32)max_height * window_aspect_ratio);
mThumbnailWidth = llround((F32)max_height * aspect_ratio);
}
if(mThumbnailWidth > window_width || mThumbnailHeight > window_height)
if (mThumbnailWidth > width || mThumbnailHeight > height)
{
return FALSE ;//if the window is too small, ignore thumbnail updating.
}
S32 left = 0 , top = mThumbnailHeight, right = mThumbnailWidth, bottom = 0 ;
if(!mKeepAspectRatio)
if (!mKeepAspectRatio)
{
F32 ratio_x = (F32)getWidth() / window_width ;
F32 ratio_y = (F32)getHeight() / window_height ;
F32 ratio_x = (F32)getWidth() / width ;
F32 ratio_y = (F32)getHeight() / height ;
//if(getWidth() > window_width ||
// getHeight() > window_height )
{
if(ratio_x > ratio_y)
{
top = (S32)(top * ratio_y / ratio_x) ;
}
else
{
right = (S32)(right * ratio_x / ratio_y) ;
}
}
//else
//{
// right = (S32)(right * ratio_x) ;
// top = (S32)(top * ratio_y) ;
//}
if (ratio_x > ratio_y)
{
top = (S32)(top * ratio_y / ratio_x) ;
}
else
{
right = (S32)(right * ratio_x / ratio_y) ;
}
left = (S32)((mThumbnailWidth - right) * 0.5f) ;
bottom = (S32)((mThumbnailHeight - top) * 0.5f) ;
top += bottom ;
@ -556,25 +544,62 @@ void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update)
return ;
}
// Invalidate the big thumbnail when we regenerate the small one
mBigThumbnailUpToDate = FALSE;
if(mThumbnailImage)
{
resetThumbnailImage() ;
}
LLPointer<LLImageRaw> raw = new LLImageRaw;
if(!gViewerWindow->thumbnailSnapshot(raw,
mThumbnailWidth, mThumbnailHeight,
gSavedSettings.getBOOL("RenderUIInSnapshot"),
FALSE,
mSnapshotBufferType) )
if (mThumbnailSubsampled)
{
// The thumbnail is be a subsampled version of the preview (used in SL Share previews, i.e. Flickr, Twitter, Facebook)
raw->resize( mPreviewImage->getWidth(),
mPreviewImage->getHeight(),
mPreviewImage->getComponents());
raw->copy(mPreviewImage);
// Scale to the thumbnail size
if (!raw->scale(mThumbnailWidth, mThumbnailHeight))
{
raw = NULL ;
}
}
else
{
// The thumbnail is a screen view with screen grab positioning preview
if(!gViewerWindow->thumbnailSnapshot(raw,
mThumbnailWidth, mThumbnailHeight,
mAllowRenderUI && gSavedSettings.getBOOL("RenderUIInSnapshot"),
FALSE,
mSnapshotBufferType) )
{
raw = NULL ;
}
}
if (raw)
{
raw = NULL ;
}
if(raw)
{
raw->expandToPowerOfTwo();
mThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE);
// Filter the thumbnail
// Note: filtering needs to be done *before* the scaling to power of 2 or the effect is distorted
if (getFilter() != "")
{
std::string filter_path = LLImageFiltersManager::getInstance()->getFilterPath(getFilter());
if (filter_path != "")
{
LLImageFilter filter(filter_path);
filter.executeFilter(raw);
}
else
{
llwarns << "Couldn't find a path to the following filter : " << getFilter() << llendl;
}
}
// Scale to a power of 2 so it can be mapped to a texture
raw->expandToPowerOfTwo();
mThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE);
mThumbnailUpToDate = TRUE ;
}
@ -582,6 +607,52 @@ void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update)
mThumbnailUpdateLock = FALSE ;
}
LLViewerTexture* LLSnapshotLivePreview::getBigThumbnailImage()
{
if (mThumbnailUpdateLock) //in the process of updating
{
return NULL;
}
if (mBigThumbnailUpToDate && mBigThumbnailImage)//already updated
{
return mBigThumbnailImage;
}
LLPointer<LLImageRaw> raw = new LLImageRaw;
if (raw)
{
// The big thumbnail is a new filtered version of the preview (used in SL Share previews, i.e. Flickr, Twitter, Facebook)
mBigThumbnailWidth = mPreviewImage->getWidth();
mBigThumbnailHeight = mPreviewImage->getHeight();
raw->resize( mBigThumbnailWidth,
mBigThumbnailHeight,
mPreviewImage->getComponents());
raw->copy(mPreviewImage);
// Filter
// Note: filtering needs to be done *before* the scaling to power of 2 or the effect is distorted
if (getFilter() != "")
{
std::string filter_path = LLImageFiltersManager::getInstance()->getFilterPath(getFilter());
if (filter_path != "")
{
LLImageFilter filter(filter_path);
filter.executeFilter(raw);
}
else
{
llwarns << "Couldn't find a path to the following filter : " << getFilter() << llendl;
}
}
// Scale to a power of 2 so it can be mapped to a texture
raw->expandToPowerOfTwo();
mBigThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE);
mBigThumbnailUpToDate = TRUE ;
}
return mBigThumbnailImage ;
}
// Called often. Checks whether it's time to grab a new snapshot and if so, does it.
// Returns TRUE if new snapshot generated, FALSE otherwise.
@ -598,7 +669,7 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview )
// If we're in freeze-frame mode and camera has moved, update snapshot.
LLVector3 new_camera_pos = LLViewerCamera::getInstance()->getOrigin();
LLQuaternion new_camera_rot = LLViewerCamera::getInstance()->getQuaternion();
if (gSavedSettings.getBOOL("FreezeTime") &&
if (gSavedSettings.getBOOL("FreezeTime") && previewp->mAllowFullScreenPreview &&
(new_camera_pos != previewp->mCameraPos || dot(new_camera_rot, previewp->mCameraRot) < 0.995f))
{
previewp->mCameraPos = new_camera_pos;
@ -616,157 +687,256 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview )
previewp->mSnapshotActive =
(previewp->mSnapshotDelayTimer.getStarted() && previewp->mSnapshotDelayTimer.hasExpired())
&& !LLToolCamera::getInstance()->hasMouseCapture(); // don't take snapshots while ALT-zoom active
if ( ! previewp->mSnapshotActive)
if (!previewp->mSnapshotActive && previewp->getSnapshotUpToDate() && previewp->getThumbnailUpToDate())
{
return FALSE;
}
// time to produce a snapshot
previewp->setThumbnailImageSize();
if(!previewp->getSnapshotUpToDate())
{
lldebugs << "producing snapshot" << llendl;
if (!previewp->mPreviewImage)
{
previewp->mPreviewImage = new LLImageRaw;
}
LL_DEBUGS() << "producing snapshot" << LL_ENDL;
if (!previewp->mPreviewImage)
previewp->setVisible(FALSE);
previewp->setEnabled(FALSE);
previewp->getWindow()->incBusyCount();
previewp->setImageScaled(FALSE);
// grab the raw image
if (gViewerWindow->rawSnapshot(
previewp->mPreviewImage,
previewp->getWidth(),
previewp->getHeight(),
previewp->mKeepAspectRatio,//gSavedSettings.getBOOL("KeepAspectForSnapshot"),
previewp->getSnapshotType() == LLSnapshotLivePreview::SNAPSHOT_TEXTURE,
previewp->mAllowRenderUI && gSavedSettings.getBOOL("RenderUIInSnapshot"),
FALSE,
previewp->mSnapshotBufferType,
previewp->getMaxImageSize()))
{
// Invalidate/delete any existing encoded image
previewp->mPreviewImageEncoded = NULL;
// Invalidate/delete any existing formatted image
previewp->mFormattedImage = NULL;
// Update the data size
previewp->estimateDataSize();
// Full size preview is set: get the decoded image result and save it for animation
if (gSavedSettings.getBOOL("UseFreezeFrame") && previewp->mAllowFullScreenPreview)
{
// Get the decoded version of the formatted image
previewp->getEncodedImage();
// We need to scale that a bit for display...
LLPointer<LLImageRaw> scaled = new LLImageRaw(
previewp->mPreviewImageEncoded->getData(),
previewp->mPreviewImageEncoded->getWidth(),
previewp->mPreviewImageEncoded->getHeight(),
previewp->mPreviewImageEncoded->getComponents());
if (!scaled->isBufferInvalid())
{
// leave original image dimensions, just scale up texture buffer
if (previewp->mPreviewImageEncoded->getWidth() > 1024 || previewp->mPreviewImageEncoded->getHeight() > 1024)
{
// go ahead and shrink image to appropriate power of 2 for display
scaled->biasedScaleToPowerOfTwo(1024);
previewp->setImageScaled(TRUE);
}
else
{
// expand image but keep original image data intact
scaled->expandToPowerOfTwo(1024, FALSE);
}
previewp->mViewerImage[previewp->mCurImageIndex] = LLViewerTextureManager::getLocalTexture(scaled.get(), FALSE);
LLPointer<LLViewerTexture> curr_preview_image = previewp->mViewerImage[previewp->mCurImageIndex];
gGL.getTexUnit(0)->bind(curr_preview_image);
curr_preview_image->setFilteringOption(previewp->getSnapshotType() == SNAPSHOT_TEXTURE ? LLTexUnit::TFO_ANISOTROPIC : LLTexUnit::TFO_POINT);
curr_preview_image->setAddressMode(LLTexUnit::TAM_CLAMP);
previewp->mPosTakenGlobal = gAgentCamera.getCameraPositionGlobal();
previewp->mShineCountdown = 4; // wait a few frames to avoid animation glitch due to readback this frame
}
}
// The snapshot is updated now...
previewp->mSnapshotUpToDate = TRUE;
// We need to update the thumbnail though
previewp->setThumbnailImageSize();
previewp->generateThumbnailImage(TRUE) ;
}
previewp->getWindow()->decBusyCount();
previewp->setVisible(gSavedSettings.getBOOL("UseFreezeFrame") && previewp->mAllowFullScreenPreview); // only show fullscreen preview when in freeze frame mode
previewp->mSnapshotDelayTimer.stop();
previewp->mSnapshotActive = FALSE;
lldebugs << "done creating snapshot" << llendl;
}
if (!previewp->getThumbnailUpToDate())
{
previewp->mPreviewImage = new LLImageRaw;
previewp->generateThumbnailImage() ;
}
// Tell the floater container that the snapshot is updated now
if (previewp->mViewContainer)
{
previewp->mViewContainer->notify(LLSD().with("snapshot-updated", true));
}
if (!previewp->mPreviewImageEncoded)
return TRUE;
}
S32 LLSnapshotLivePreview::getEncodedImageWidth() const
{
S32 width = getWidth();
if (getSnapshotType() == SNAPSHOT_TEXTURE)
{
width = LLImageRaw::biasedDimToPowerOfTwo(width,MAX_TEXTURE_SIZE);
}
return width;
}
S32 LLSnapshotLivePreview::getEncodedImageHeight() const
{
S32 height = getHeight();
if (getSnapshotType() == SNAPSHOT_TEXTURE)
{
height = LLImageRaw::biasedDimToPowerOfTwo(height,MAX_TEXTURE_SIZE);
}
return height;
}
LLPointer<LLImageRaw> LLSnapshotLivePreview::getEncodedImage()
{
if (!mPreviewImageEncoded)
{
previewp->mPreviewImageEncoded = new LLImageRaw;
}
previewp->setVisible(FALSE);
previewp->setEnabled(FALSE);
previewp->getWindow()->incBusyCount();
previewp->setImageScaled(FALSE);
// grab the raw image and encode it into desired format
if(gViewerWindow->rawSnapshot(
previewp->mPreviewImage,
previewp->getWidth(),
previewp->getHeight(),
previewp->mKeepAspectRatio,//gSavedSettings.getBOOL("KeepAspectForSnapshot"),
previewp->getSnapshotType() == LLSnapshotLivePreview::SNAPSHOT_TEXTURE,
gSavedSettings.getBOOL("RenderUIInSnapshot"),
FALSE,
previewp->mSnapshotBufferType,
previewp->getMaxImageSize()))
{
previewp->mPreviewImageEncoded->resize(
previewp->mPreviewImage->getWidth(),
previewp->mPreviewImage->getHeight(),
previewp->mPreviewImage->getComponents());
if(previewp->getSnapshotType() == SNAPSHOT_TEXTURE)
mPreviewImageEncoded = new LLImageRaw;
mPreviewImageEncoded->resize(
mPreviewImage->getWidth(),
mPreviewImage->getHeight(),
mPreviewImage->getComponents());
if (getSnapshotType() == SNAPSHOT_TEXTURE)
{
// We don't store the intermediate formatted image in mFormattedImage in the J2C case
LL_DEBUGS() << "Encoding new image of format J2C" << LL_ENDL;
LLPointer<LLImageJ2C> formatted = new LLImageJ2C;
// Copy the preview
LLPointer<LLImageRaw> scaled = new LLImageRaw(
previewp->mPreviewImage->getData(),
previewp->mPreviewImage->getWidth(),
previewp->mPreviewImage->getHeight(),
previewp->mPreviewImage->getComponents());
mPreviewImage->getData(),
mPreviewImage->getWidth(),
mPreviewImage->getHeight(),
mPreviewImage->getComponents());
// Scale it as required by J2C
scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE);
previewp->setImageScaled(TRUE);
setImageScaled(TRUE);
// Compress to J2C
if (formatted->encode(scaled, 0.f))
{
previewp->mDataSize = formatted->getDataSize();
formatted->decode(previewp->mPreviewImageEncoded, 0);
// We can update the data size precisely at that point
mDataSize = formatted->getDataSize();
// Decompress back
formatted->decode(mPreviewImageEncoded, 0);
}
}
else
{
// delete any existing image
previewp->mFormattedImage = NULL;
// now create the new one of the appropriate format.
LLFloaterSnapshot::ESnapshotFormat format = previewp->getSnapshotFormat();
LL_DEBUGS() << "Encoding new image of format " << format << LL_ENDL;
switch(format)
{
case LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG:
previewp->mFormattedImage = new LLImagePNG();
break;
case LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG:
previewp->mFormattedImage = new LLImageJPEG(previewp->mSnapshotQuality);
break;
case LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP:
previewp->mFormattedImage = new LLImageBMP();
break;
}
if (previewp->mFormattedImage->encode(previewp->mPreviewImage, 0))
{
previewp->mDataSize = previewp->mFormattedImage->getDataSize();
// special case BMP to copy instead of decode otherwise decode will crash.
if(format == LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP)
{
previewp->mPreviewImageEncoded->copy(previewp->mPreviewImage);
}
else
{
previewp->mFormattedImage->decode(previewp->mPreviewImageEncoded, 0);
}
}
}
LLPointer<LLImageRaw> scaled = new LLImageRaw(
previewp->mPreviewImageEncoded->getData(),
previewp->mPreviewImageEncoded->getWidth(),
previewp->mPreviewImageEncoded->getHeight(),
previewp->mPreviewImageEncoded->getComponents());
if(!scaled->isBufferInvalid())
{
// leave original image dimensions, just scale up texture buffer
if (previewp->mPreviewImageEncoded->getWidth() > 1024 || previewp->mPreviewImageEncoded->getHeight() > 1024)
{
// go ahead and shrink image to appropriate power of 2 for display
scaled->biasedScaleToPowerOfTwo(1024);
previewp->setImageScaled(TRUE);
}
else
{
// expand image but keep original image data intact
scaled->expandToPowerOfTwo(1024, FALSE);
}
previewp->mViewerImage[previewp->mCurImageIndex] = LLViewerTextureManager::getLocalTexture(scaled.get(), FALSE);
LLPointer<LLViewerTexture> curr_preview_image = previewp->mViewerImage[previewp->mCurImageIndex];
gGL.getTexUnit(0)->bind(curr_preview_image);
if (previewp->getSnapshotType() != SNAPSHOT_TEXTURE)
{
curr_preview_image->setFilteringOption(LLTexUnit::TFO_POINT);
}
else
{
curr_preview_image->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
}
curr_preview_image->setAddressMode(LLTexUnit::TAM_CLAMP);
previewp->mSnapshotUpToDate = TRUE;
previewp->generateThumbnailImage(TRUE) ;
previewp->mPosTakenGlobal = gAgentCamera.getCameraPositionGlobal();
previewp->mShineCountdown = 4; // wait a few frames to avoid animation glitch due to readback this frame
// Update mFormattedImage if necessary
getFormattedImage();
if (getSnapshotFormat() == LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP)
{
// BMP hack : copy instead of decode otherwise decode will crash.
mPreviewImageEncoded->copy(mPreviewImage);
}
else
{
// Decode back
mFormattedImage->decode(mPreviewImageEncoded, 0);
}
}
}
previewp->getWindow()->decBusyCount();
// only show fullscreen preview when in freeze frame mode
previewp->setVisible(gSavedSettings.getBOOL("UseFreezeFrame"));
previewp->mSnapshotDelayTimer.stop();
previewp->mSnapshotActive = FALSE;
return mPreviewImageEncoded;
}
if(!previewp->getThumbnailUpToDate())
{
previewp->generateThumbnailImage() ;
}
LL_DEBUGS() << "done creating snapshot" << LL_ENDL;
LLFloaterSnapshot::postUpdate();
LLFloaterSocial::postUpdate();
// We actually estimate the data size so that we do not require actual compression when showing the preview
// Note : whenever formatted image is computed, mDataSize will be updated to reflect the true size
void LLSnapshotLivePreview::estimateDataSize()
{
// Compression ratio
F32 ratio = 1.0;
if (getSnapshotType() == SNAPSHOT_TEXTURE)
{
ratio = 8.0; // This is what we shoot for when compressing to J2C
}
else
{
LLFloaterSnapshot::ESnapshotFormat format = getSnapshotFormat();
switch (format)
{
case LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG:
ratio = 3.0; // Average observed PNG compression ratio
break;
case LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG:
// Observed from JPG compression tests
ratio = (110 - mSnapshotQuality) / 2;
break;
case LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP:
ratio = 1.0; // No compression with BMP
break;
}
}
mDataSize = (S32)((F32)mPreviewImage->getDataSize() / ratio);
}
return TRUE;
LLPointer<LLImageFormatted> LLSnapshotLivePreview::getFormattedImage()
{
if (!mFormattedImage)
{
// Apply the filter to mPreviewImage
if (getFilter() != "")
{
std::string filter_path = LLImageFiltersManager::getInstance()->getFilterPath(getFilter());
if (filter_path != "")
{
LLImageFilter filter(filter_path);
filter.executeFilter(mPreviewImage);
}
else
{
llwarns << "Couldn't find a path to the following filter : " << getFilter() << llendl;
}
}
// Create the new formatted image of the appropriate format.
LLFloaterSnapshot::ESnapshotFormat format = getSnapshotFormat();
lldebugs << "Encoding new image of format " << format << llendl;
switch (format)
{
case LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG:
mFormattedImage = new LLImagePNG();
break;
case LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG:
mFormattedImage = new LLImageJPEG(mSnapshotQuality);
break;
case LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP:
mFormattedImage = new LLImageBMP();
break;
}
if (mFormattedImage->encode(mPreviewImage, 0))
{
// We can update the data size precisely at that point
mDataSize = mFormattedImage->getDataSize();
}
}
return mFormattedImage;
}
void LLSnapshotLivePreview::setSize(S32 w, S32 h)
@ -776,6 +946,15 @@ void LLSnapshotLivePreview::setSize(S32 w, S32 h)
setHeight(h);
}
void LLSnapshotLivePreview::setSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat format)
{
if (mSnapshotFormat != format)
{
mFormattedImage = NULL; // Invalidate the already formatted image if any
mSnapshotFormat = format;
}
}
void LLSnapshotLivePreview::getSize(S32& w, S32& h) const
{
w = getWidth();
@ -836,6 +1015,10 @@ void LLSnapshotLivePreview::saveTexture()
BOOL LLSnapshotLivePreview::saveLocal()
{
// Update mFormattedImage if necessary
getFormattedImage();
// Save the formatted image
BOOL success = gViewerWindow->saveImageNumbered(mFormattedImage);
if(success)
@ -844,3 +1027,4 @@ BOOL LLSnapshotLivePreview::saveLocal()
}
return success;
}

View File

@ -28,6 +28,7 @@
#define LL_LLSNAPSHOTLIVEPREVIEW_H
#include "llpanelsnapshot.h"
#include "llviewertexture.h"
#include "llviewerwindow.h"
class LLImageJPEG;
@ -62,6 +63,8 @@ public:
LLSnapshotLivePreview(const LLSnapshotLivePreview::Params& p);
~LLSnapshotLivePreview();
void setContainer(LLView* container) { mViewContainer = container; }
/*virtual*/ void draw();
/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
@ -71,6 +74,9 @@ public:
void getSize(S32& w, S32& h) const;
S32 getWidth() const { return mWidth[mCurImageIndex]; }
S32 getHeight() const { return mHeight[mCurImageIndex]; }
S32 getEncodedImageWidth() const;
S32 getEncodedImageHeight() const;
void estimateDataSize();
S32 getDataSize() const { return mDataSize; }
void setMaxImageSize(S32 size) ;
S32 getMaxImageSize() {return mMaxImageSize ;}
@ -84,36 +90,48 @@ public:
S32 getThumbnailHeight() const { return mThumbnailHeight ; }
BOOL getThumbnailLock() const { return mThumbnailUpdateLock ; }
BOOL getThumbnailUpToDate() const { return mThumbnailUpToDate ;}
void setThumbnailSubsampled(BOOL subsampled) { mThumbnailSubsampled = subsampled; }
LLViewerTexture* getCurrentImage();
F32 getImageAspect();
F32 getAspect() ;
const LLRect& getImageRect() const { return mImageRect[mCurImageIndex]; }
BOOL isImageScaled() const { return mImageScaled[mCurImageIndex]; }
void setImageScaled(BOOL scaled) { mImageScaled[mCurImageIndex] = scaled; }
const LLVector3d& getPosTakenGlobal() const { return mPosTakenGlobal; }
void setSnapshotType(ESnapshotType type) { mSnapshotType = type; }
void setSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat type) { mSnapshotFormat = type; }
void setSnapshotQuality(S32 quality);
void setSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat format);
bool setSnapshotQuality(S32 quality, bool set_by_user = true);
void setSnapshotBufferType(LLViewerWindow::ESnapshotType type) { mSnapshotBufferType = type; }
void setAllowRenderUI(BOOL allow) { mAllowRenderUI = allow; }
void setAllowFullScreenPreview(BOOL allow) { mAllowFullScreenPreview = allow; }
void setFilter(std::string filter_name) { mFilterName = filter_name; }
std::string getFilter() const { return mFilterName; }
void updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail = FALSE, F32 delay = 0.f);
void saveTexture();
BOOL saveLocal();
LLPointer<LLImageFormatted> getFormattedImage() const { return mFormattedImage; }
LLPointer<LLImageRaw> getEncodedImage() const { return mPreviewImageEncoded; }
LLPointer<LLImageFormatted> getFormattedImage();
LLPointer<LLImageRaw> getEncodedImage();
/// Sets size of preview thumbnail image and thhe surrounding rect.
/// Sets size of preview thumbnail image and the surrounding rect.
void setThumbnailPlaceholderRect(const LLRect& rect) {mThumbnailPlaceholderRect = rect; }
BOOL setThumbnailImageSize() ;
void generateThumbnailImage(BOOL force_update = FALSE) ;
void resetThumbnailImage() { mThumbnailImage = NULL ; }
void drawPreviewRect(S32 offset_x, S32 offset_y) ;
LLViewerTexture* getBigThumbnailImage();
S32 getBigThumbnailWidth() const { return mBigThumbnailWidth ; }
S32 getBigThumbnailHeight() const { return mBigThumbnailHeight ; }
// Returns TRUE when snapshot generated, FALSE otherwise.
static BOOL onIdle( void* snapshot_preview );
private:
LLView* mViewContainer;
LLColor4 mColor;
LLPointer<LLViewerTexture> mViewerImage[2]; //used to represent the scene when the frame is frozen.
LLRect mImageRect[2];
@ -130,11 +148,20 @@ private:
BOOL mThumbnailUpdateLock ;
BOOL mThumbnailUpToDate ;
LLRect mThumbnailPlaceholderRect;
BOOL mThumbnailSubsampled; // TRUE if the thumbnail is a subsampled version of the mPreviewImage
LLPointer<LLViewerTexture> mBigThumbnailImage ;
S32 mBigThumbnailWidth;
S32 mBigThumbnailHeight;
BOOL mBigThumbnailUpToDate;
S32 mCurImageIndex;
// The logic is mPreviewImage (raw frame) -> mFormattedImage (formatted / filtered) -> mPreviewImageEncoded (decoded back, to show artifacts)
LLPointer<LLImageRaw> mPreviewImage;
LLPointer<LLImageRaw> mPreviewImageEncoded;
LLPointer<LLImageFormatted> mFormattedImage;
BOOL mAllowRenderUI;
BOOL mAllowFullScreenPreview;
LLFrameTimer mSnapshotDelayTimer;
S32 mShineCountdown;
LLFrameTimer mShineAnimTimer;
@ -151,6 +178,7 @@ private:
LLQuaternion mCameraRot;
BOOL mSnapshotActive;
LLViewerWindow::ESnapshotType mSnapshotBufferType;
std::string mFilterName;
public:
static std::set<LLSnapshotLivePreview*> sList;

View File

@ -2608,7 +2608,8 @@ bool LLTextureFetch::createRequest(FTType f_type, const std::string& url, const
S32 desired_size;
std::string exten = gDirUtilp->getExtension(url);
if (f_type == FTT_SERVER_BAKE)
//if (f_type == FTT_SERVER_BAKE)
if ((f_type == FTT_SERVER_BAKE) && !url.empty() && !exten.empty() && (LLImageBase::getCodecFromExtension(exten) != IMG_CODEC_J2C))
{
// SH-4030: This case should be redundant with the following one, just
// breaking it out here to clarify that it's intended behavior.

View File

@ -0,0 +1,503 @@
/**
* @file lltwitterconnect.h
* @author Merov, Cho
* @brief Connection to Twitter Service
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "lltwitterconnect.h"
#include "llagent.h"
#include "llcallingcard.h" // for LLAvatarTracker
#include "llcommandhandler.h"
#include "llhttpclient.h"
#include "llnotificationsutil.h"
#include "llurlaction.h"
#include "llimagepng.h"
#include "llimagejpeg.h"
#include "lltrans.h"
#include "llevents.h"
#include "llviewerregion.h"
#include "llfloaterwebcontent.h"
#include "llfloaterreg.h"
boost::scoped_ptr<LLEventPump> LLTwitterConnect::sStateWatcher(new LLEventStream("TwitterConnectState"));
boost::scoped_ptr<LLEventPump> LLTwitterConnect::sInfoWatcher(new LLEventStream("TwitterConnectInfo"));
boost::scoped_ptr<LLEventPump> LLTwitterConnect::sContentWatcher(new LLEventStream("TwitterConnectContent"));
// Local functions
void log_twitter_connect_error(const std::string& request, U32 status, const std::string& reason, const std::string& code, const std::string& description)
{
// Note: 302 (redirect) is *not* an error that warrants logging
if (status != 302)
{
LL_WARNS("TwitterConnect") << request << " request failed with a " << status << " " << reason << ". Reason: " << code << " (" << description << ")" << LL_ENDL;
}
}
void toast_user_for_twitter_success()
{
LLSD args;
args["MESSAGE"] = LLTrans::getString("twitter_post_success");
LLNotificationsUtil::add("TwitterConnect", args);
}
///////////////////////////////////////////////////////////////////////////////
//
class LLTwitterConnectResponder : public LLHTTPClient::Responder
{
LOG_CLASS(LLTwitterConnectResponder);
public:
LLTwitterConnectResponder()
{
LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS);
}
/* virtual */ void httpSuccess()
{
LL_DEBUGS("TwitterConnect") << "Connect successful. " << dumpResponse() << LL_ENDL;
LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTED);
}
/* virtual */ void httpFailure()
{
if ( HTTP_FOUND == getStatus() )
{
const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION);
if (location.empty())
{
LL_WARNS("TwitterConnect") << "Missing Location header " << dumpResponse()
<< "[headers:" << getResponseHeaders() << "]" << LL_ENDL;
}
else
{
LLTwitterConnect::instance().openTwitterWeb(location);
}
}
else
{
LL_WARNS("TwitterConnect") << dumpResponse() << LL_ENDL;
LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_FAILED);
const LLSD& content = getContent();
log_twitter_connect_error("Connect", getStatus(), getReason(),
content.get("error_code"), content.get("error_description"));
}
}
};
///////////////////////////////////////////////////////////////////////////////
//
class LLTwitterShareResponder : public LLHTTPClient::Responder
{
LOG_CLASS(LLTwitterShareResponder);
public:
LLTwitterShareResponder()
{
LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_POSTING);
}
/* virtual */ void httpSuccess()
{
toast_user_for_twitter_success();
LL_DEBUGS("TwitterConnect") << "Post successful. " << dumpResponse() << LL_ENDL;
LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_POSTED);
}
/* virtual */ void httpFailure()
{
if ( HTTP_FOUND == getStatus() )
{
const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION);
if (location.empty())
{
LL_WARNS("TwitterConnect") << "Missing Location header " << dumpResponse()
<< "[headers:" << getResponseHeaders() << "]" << LL_ENDL;
}
else
{
LLTwitterConnect::instance().openTwitterWeb(location);
}
}
else if ( HTTP_NOT_FOUND == getStatus() )
{
LLTwitterConnect::instance().connectToTwitter();
}
else
{
LL_WARNS("TwitterConnect") << dumpResponse() << LL_ENDL;
LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_POST_FAILED);
const LLSD& content = getContent();
log_twitter_connect_error("Share", getStatus(), getReason(),
content.get("error_code"), content.get("error_description"));
}
}
};
///////////////////////////////////////////////////////////////////////////////
//
class LLTwitterDisconnectResponder : public LLHTTPClient::Responder
{
LOG_CLASS(LLTwitterDisconnectResponder);
public:
LLTwitterDisconnectResponder()
{
LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_DISCONNECTING);
}
void setUserDisconnected()
{
// Clear data
LLTwitterConnect::instance().clearInfo();
//Notify state change
LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_NOT_CONNECTED);
}
/* virtual */ void httpSuccess()
{
LL_DEBUGS("TwitterConnect") << "Disconnect successful. " << dumpResponse() << LL_ENDL;
setUserDisconnected();
}
/* virtual */ void httpFailure()
{
//User not found so already disconnected
if ( HTTP_NOT_FOUND == getStatus() )
{
LL_DEBUGS("TwitterConnect") << "Already disconnected. " << dumpResponse() << LL_ENDL;
setUserDisconnected();
}
else
{
LL_WARNS("TwitterConnect") << dumpResponse() << LL_ENDL;
LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_DISCONNECT_FAILED);
const LLSD& content = getContent();
log_twitter_connect_error("Disconnect", getStatus(), getReason(),
content.get("error_code"), content.get("error_description"));
}
}
};
///////////////////////////////////////////////////////////////////////////////
//
class LLTwitterConnectedResponder : public LLHTTPClient::Responder
{
LOG_CLASS(LLTwitterConnectedResponder);
public:
LLTwitterConnectedResponder(bool auto_connect) : mAutoConnect(auto_connect)
{
LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS);
}
/* virtual */ void httpSuccess()
{
LL_DEBUGS("TwitterConnect") << "Connect successful. " << dumpResponse() << LL_ENDL;
LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTED);
}
/* virtual */ void httpFailure()
{
// show the facebook login page if not connected yet
if ( HTTP_NOT_FOUND == getStatus() )
{
LL_DEBUGS("TwitterConnect") << "Not connected. " << dumpResponse() << LL_ENDL;
if (mAutoConnect)
{
LLTwitterConnect::instance().connectToTwitter();
}
else
{
LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_NOT_CONNECTED);
}
}
else
{
LL_WARNS("TwitterConnect") << dumpResponse() << LL_ENDL;
LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_FAILED);
const LLSD& content = getContent();
log_twitter_connect_error("Connected", getStatus(), getReason(),
content.get("error_code"), content.get("error_description"));
}
}
private:
bool mAutoConnect;
};
///////////////////////////////////////////////////////////////////////////////
//
class LLTwitterInfoResponder : public LLHTTPClient::Responder
{
LOG_CLASS(LLTwitterInfoResponder);
public:
/* virtual */ void httpSuccess()
{
LL_INFOS("TwitterConnect") << "Twitter: Info received" << LL_ENDL;
LL_DEBUGS("TwitterConnect") << "Getting Twitter info successful. " << dumpResponse() << LL_ENDL;
LLTwitterConnect::instance().storeInfo(getContent());
}
/* virtual */ void httpFailure()
{
if ( HTTP_FOUND == getStatus() )
{
const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION);
if (location.empty())
{
LL_WARNS("TwitterConnect") << "Missing Location header " << dumpResponse()
<< "[headers:" << getResponseHeaders() << "]" << LL_ENDL;
}
else
{
LLTwitterConnect::instance().openTwitterWeb(location);
}
}
else
{
LL_WARNS("TwitterConnect") << dumpResponse() << LL_ENDL;
const LLSD& content = getContent();
log_twitter_connect_error("Info", getStatus(), getReason(),
content.get("error_code"), content.get("error_description"));
}
}
};
///////////////////////////////////////////////////////////////////////////////
//
LLTwitterConnect::LLTwitterConnect()
: mConnectionState(TWITTER_NOT_CONNECTED),
mConnected(false),
mInfo(),
mRefreshInfo(false),
mReadFromMaster(false)
{
}
void LLTwitterConnect::openTwitterWeb(std::string url)
{
// Open the URL in an internal browser window without navigation UI
LLFloaterWebContent::Params p;
p.url(url);
p.show_chrome(true);
p.allow_address_entry(false);
p.allow_back_forward_navigation(false);
p.trusted_content(true);
p.clean_browser(true);
LLFloater *floater = LLFloaterReg::showInstance("twitter_web", p);
//the internal web browser has a bug that prevents it from gaining focus unless a mouse event occurs first (it seems).
//So when showing the internal web browser, set focus to it's containing floater "twitter_web". When a mouse event
//occurs on the "webbrowser" panel part of the floater, a mouse cursor will properly show and the "webbrowser" will gain focus.
//twitter_web floater contains the "webbrowser" panel. JIRA: ACME-744
gFocusMgr.setKeyboardFocus( floater );
//LLUrlAction::openURLExternal(url);
}
std::string LLTwitterConnect::getTwitterConnectURL(const std::string& route, bool include_read_from_master)
{
std::string url("");
LLViewerRegion *regionp = gAgent.getRegion();
if (regionp)
{
//url = "http://pdp15.lindenlab.com/twitter/agent/" + gAgentID.asString(); // TEMPORARY FOR TESTING - CHO
url = regionp->getCapability("TwitterConnect");
url += route;
if (include_read_from_master && mReadFromMaster)
{
url += "?read_from_master=true";
}
}
return url;
}
void LLTwitterConnect::connectToTwitter(const std::string& request_token, const std::string& oauth_verifier)
{
LLSD body;
if (!request_token.empty())
body["request_token"] = request_token;
if (!oauth_verifier.empty())
body["oauth_verifier"] = oauth_verifier;
LLHTTPClient::put(getTwitterConnectURL("/connection"), body, new LLTwitterConnectResponder());
}
void LLTwitterConnect::disconnectFromTwitter()
{
LLHTTPClient::del(getTwitterConnectURL("/connection"), new LLTwitterDisconnectResponder());
}
void LLTwitterConnect::checkConnectionToTwitter(bool auto_connect)
{
const bool follow_redirects = false;
const F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
LLHTTPClient::get(getTwitterConnectURL("/connection", true), new LLTwitterConnectedResponder(auto_connect),
LLSD(), timeout, follow_redirects);
}
void LLTwitterConnect::loadTwitterInfo()
{
if(mRefreshInfo)
{
const bool follow_redirects = false;
const F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
LLHTTPClient::get(getTwitterConnectURL("/info", true), new LLTwitterInfoResponder(),
LLSD(), timeout, follow_redirects);
}
}
void LLTwitterConnect::uploadPhoto(const std::string& image_url, const std::string& status)
{
LLSD body;
body["image"] = image_url;
body["status"] = status;
// Note: we can use that route for different publish action. We should be able to use the same responder.
LLHTTPClient::post(getTwitterConnectURL("/share/photo", true), body, new LLTwitterShareResponder());
}
void LLTwitterConnect::uploadPhoto(LLPointer<LLImageFormatted> image, const std::string& status)
{
std::string imageFormat;
if (dynamic_cast<LLImagePNG*>(image.get()))
{
imageFormat = "png";
}
else if (dynamic_cast<LLImageJPEG*>(image.get()))
{
imageFormat = "jpg";
}
else
{
llwarns << "Image to upload is not a PNG or JPEG" << llendl;
return;
}
// All this code is mostly copied from LLWebProfile::post()
const std::string boundary = "----------------------------0123abcdefab";
LLSD headers;
headers["Content-Type"] = "multipart/form-data; boundary=" + boundary;
std::ostringstream body;
// *NOTE: The order seems to matter.
body << "--" << boundary << "\r\n"
<< "Content-Disposition: form-data; name=\"status\"\r\n\r\n"
<< status << "\r\n";
body << "--" << boundary << "\r\n"
<< "Content-Disposition: form-data; name=\"image\"; filename=\"Untitled." << imageFormat << "\"\r\n"
<< "Content-Type: image/" << imageFormat << "\r\n\r\n";
// Insert the image data.
// *FIX: Treating this as a string will probably screw it up ...
U8* image_data = image->getData();
for (S32 i = 0; i < image->getDataSize(); ++i)
{
body << image_data[i];
}
body << "\r\n--" << boundary << "--\r\n";
// postRaw() takes ownership of the buffer and releases it later.
size_t size = body.str().size();
U8 *data = new U8[size];
memcpy(data, body.str().data(), size);
// Note: we can use that route for different publish action. We should be able to use the same responder.
LLHTTPClient::postRaw(getTwitterConnectURL("/share/photo", true), data, size, new LLTwitterShareResponder(), headers);
}
void LLTwitterConnect::updateStatus(const std::string& status)
{
LLSD body;
body["status"] = status;
// Note: we can use that route for different publish action. We should be able to use the same responder.
LLHTTPClient::post(getTwitterConnectURL("/share/status", true), body, new LLTwitterShareResponder());
}
void LLTwitterConnect::storeInfo(const LLSD& info)
{
mInfo = info;
mRefreshInfo = false;
sInfoWatcher->post(info);
}
const LLSD& LLTwitterConnect::getInfo() const
{
return mInfo;
}
void LLTwitterConnect::clearInfo()
{
mInfo = LLSD();
}
void LLTwitterConnect::setDataDirty()
{
mRefreshInfo = true;
}
void LLTwitterConnect::setConnectionState(LLTwitterConnect::EConnectionState connection_state)
{
if(connection_state == TWITTER_CONNECTED)
{
mReadFromMaster = true;
setConnected(true);
setDataDirty();
}
else if(connection_state == TWITTER_NOT_CONNECTED)
{
setConnected(false);
}
else if(connection_state == TWITTER_POSTED)
{
mReadFromMaster = false;
}
if (mConnectionState != connection_state)
{
// set the connection state before notifying watchers
mConnectionState = connection_state;
LLSD state_info;
state_info["enum"] = connection_state;
sStateWatcher->post(state_info);
}
}
void LLTwitterConnect::setConnected(bool connected)
{
mConnected = connected;
}

View File

@ -0,0 +1,99 @@
/**
* @file lltwitterconnect.h
* @author Merov, Cho
* @brief Connection to Twitter Service
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, 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_LLTWITTERCONNECT_H
#define LL_LLTWITTERCONNECT_H
#include "llsingleton.h"
#include "llimage.h"
class LLEventPump;
/**
* @class LLTwitterConnect
*
* Manages authentication to, and interaction with, a web service allowing the
* the viewer to post status updates and upload photos to Twitter.
*/
class LLTwitterConnect : public LLSingleton<LLTwitterConnect>
{
LOG_CLASS(LLTwitterConnect);
public:
enum EConnectionState
{
TWITTER_NOT_CONNECTED = 0,
TWITTER_CONNECTION_IN_PROGRESS = 1,
TWITTER_CONNECTED = 2,
TWITTER_CONNECTION_FAILED = 3,
TWITTER_POSTING = 4,
TWITTER_POSTED = 5,
TWITTER_POST_FAILED = 6,
TWITTER_DISCONNECTING = 7,
TWITTER_DISCONNECT_FAILED = 8
};
void connectToTwitter(const std::string& request_token = "", const std::string& oauth_verifier = ""); // Initiate the complete Twitter connection. Please use checkConnectionToTwitter() in normal use.
void disconnectFromTwitter(); // Disconnect from the Twitter service.
void checkConnectionToTwitter(bool auto_connect = false); // Check if an access token is available on the Twitter service. If not, call connectToTwitter().
void loadTwitterInfo();
void uploadPhoto(const std::string& image_url, const std::string& status);
void uploadPhoto(LLPointer<LLImageFormatted> image, const std::string& status);
void updateStatus(const std::string& status);
void storeInfo(const LLSD& info);
const LLSD& getInfo() const;
void clearInfo();
void setDataDirty();
void setConnectionState(EConnectionState connection_state);
void setConnected(bool connected);
bool isConnected() { return mConnected; }
bool isTransactionOngoing() { return ((mConnectionState == TWITTER_CONNECTION_IN_PROGRESS) || (mConnectionState == TWITTER_POSTING) || (mConnectionState == TWITTER_DISCONNECTING)); }
EConnectionState getConnectionState() { return mConnectionState; }
void openTwitterWeb(std::string url);
private:
friend class LLSingleton<LLTwitterConnect>;
LLTwitterConnect();
~LLTwitterConnect() {};
std::string getTwitterConnectURL(const std::string& route = "", bool include_read_from_master = false);
EConnectionState mConnectionState;
BOOL mConnected;
LLSD mInfo;
bool mRefreshInfo;
bool mReadFromMaster;
static boost::scoped_ptr<LLEventPump> sStateWatcher;
static boost::scoped_ptr<LLEventPump> sInfoWatcher;
static boost::scoped_ptr<LLEventPump> sContentWatcher;
};
#endif // LL_LLTWITTERCONNECT_H

View File

@ -39,6 +39,7 @@
#include "llfloateravatar.h"
#include "llfloateravatarpicker.h"
#include "llfloateravatartextures.h"
#include "llfloaterbigpreview.h"
#include "llfloaterbeacons.h"
#include "llfloaterbuildoptions.h"
#include "llfloaterbulkpermission.h"
@ -61,6 +62,8 @@
#include "llfloatereditwater.h"
#include "llfloaterenvironmentsettings.h"
#include "llfloaterevent.h"
#include "llfloaterfacebook.h"
#include "llfloaterflickr.h"
#include "llfloaterfonttest.h"
#include "llfloatergesture.h"
#include "llfloatergodtools.h"
@ -104,7 +107,6 @@
#include "llfloatersettingsdebug.h"
#include "llfloatersidepanelcontainer.h"
#include "llfloatersnapshot.h"
#include "llfloatersocial.h"
#include "llfloatersounddevices.h"
#include "llfloaterspellchecksettings.h"
#include "llfloatertelehub.h"
@ -116,6 +118,7 @@
#include "llfloatertos.h"
#include "llfloatertoybox.h"
#include "llfloatertranslationsettings.h"
#include "llfloatertwitter.h"
#include "llfloateruipreview.h"
#include "llfloatervoiceeffect.h"
#include "llfloaterwebcontent.h"
@ -362,7 +365,6 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("sell_land", "floater_sell_land.xml", &LLFloaterSellLand::buildFloater);
LLFloaterReg::add("settings_debug", "floater_settings_debug.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSettingsDebug>);
LLFloaterReg::add("sound_devices", "floater_sound_devices.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSoundDevices>);
LLFloaterReg::add("social", "floater_social.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSocial>);
LLFloaterReg::add("stats", "floater_stats.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloater>);
LLFloaterReg::add("start_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRunQueue>);
LLFloaterReg::add("scene_load_stats", "floater_scene_load_stats.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSceneLoadStats>);
@ -372,8 +374,16 @@ void LLViewerFloaterReg::registerFloaters()
//LLFloaterReg::add("search", "floater_search.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSearch>);
LLFloaterReg::add("my_profile", "floater_my_web_profile.xml", (LLFloaterBuildFunc)&LLFloaterWebProfile::create);
LLFloaterReg::add("profile", "floater_web_profile.xml", (LLFloaterBuildFunc)&LLFloaterWebProfile::create);
LLFloaterReg::add("how_to", "floater_how_to.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
LLFloaterReg::add("how_to", "floater_how_to.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
LLFloaterReg::add("fbc_web", "floater_fbc_web.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
LLFloaterReg::add("flickr_web", "floater_fbc_web.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
LLFloaterReg::add("twitter_web", "floater_fbc_web.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
LLFloaterReg::add("facebook", "floater_facebook.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFacebook>);
LLFloaterReg::add("flickr", "floater_flickr.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFlickr>);
LLFloaterReg::add("twitter", "floater_twitter.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTwitter>);
LLFloaterReg::add("big_preview", "floater_big_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterBigPreview>);
LLFloaterUIPreviewUtil::registerFloater();
LLFloaterReg::add("upload_anim_bvh", "floater_animation_bvh_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterBvhPreview>, "upload");

View File

@ -1552,7 +1552,7 @@ void LLViewerMedia::createSpareBrowserMediaSource()
// popping up at the moment we start a media plugin.
if (!sSpareBrowserMediaSource && !gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins"))
{
// The null owner will keep the browser plugin from fully initializing
// The null owner will keep the browser plugin from fully initializing
// (specifically, it keeps LLPluginClassMedia from negotiating a size change,
// which keeps MediaPluginWebkit::initBrowserWindow from doing anything until we have some necessary data, like the background color)
sSpareBrowserMediaSource = LLViewerMediaImpl::newSourceFromMediaType(HTTP_CONTENT_TEXT_HTML, NULL, 0, 0);
@ -1561,7 +1561,7 @@ void LLViewerMedia::createSpareBrowserMediaSource()
/////////////////////////////////////////////////////////////////////////////////////////
// static
LLPluginClassMedia* LLViewerMedia::getSpareBrowserMediaSource()
LLPluginClassMedia* LLViewerMedia::getSpareBrowserMediaSource()
{
LLPluginClassMedia* result = sSpareBrowserMediaSource;
sSpareBrowserMediaSource = NULL;
@ -1610,7 +1610,7 @@ std::string LLViewerMedia::getParcelAudioURL()
// static
void LLViewerMedia::initClass()
{
gIdleCallbacks.addFunction(LLViewerMedia::updateMedia, NULL);
gIdleCallbacks.addFunction(LLViewerMedia::updateMedia, NULL);
sTeleportFinishConnection = LLViewerParcelMgr::getInstance()->
setTeleportFinishedCallback(boost::bind(&LLViewerMedia::onTeleportFinished));
}
@ -1698,7 +1698,8 @@ LLViewerMediaImpl::LLViewerMediaImpl( const LLUUID& texture_id,
mNavigateSuspendedDeferred(false),
mIsUpdated(false),
mTrustedBrowser(false),
mZoomFactor(1.0)
mZoomFactor(1.0),
mCleanBrowser(false)
{
// Set up the mute list observer if it hasn't been set up already.
@ -1822,14 +1823,16 @@ void LLViewerMediaImpl::setMediaType(const std::string& media_type)
//////////////////////////////////////////////////////////////////////////////////////////
/*static*/
LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height, const std::string target)
LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height, const std::string target, bool clean_browser)
{
std::string plugin_basename = LLMIMETypes::implType(media_type);
LLPluginClassMedia* media_source = NULL;
// HACK: we always try to keep a spare running webkit plugin around to improve launch times.
// If a spare was already created before PluginAttachDebuggerToPlugins was set, don't use it.
if(plugin_basename == "media_plugin_webkit" && !gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins"))
// Do not use a spare if launching with full viewer control (e.g. Facebook, Twitter and few others)
if ((plugin_basename == "media_plugin_webkit") &&
!gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins") && !clean_browser)
{
media_source = LLViewerMedia::getSpareBrowserMediaSource();
if(media_source)
@ -1841,7 +1844,6 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_
return media_source;
}
}
if(plugin_basename.empty())
{
LL_WARNS_ONCE("Media") << "Couldn't find plugin for media type " << media_type << LL_ENDL;
@ -1885,18 +1887,18 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_
// collect 'cookies enabled' setting from prefs and send to embedded browser
bool cookies_enabled = gSavedSettings.getBOOL( "CookiesEnabled" );
media_source->enable_cookies( cookies_enabled );
media_source->enable_cookies( cookies_enabled || clean_browser);
// collect 'plugins enabled' setting from prefs and send to embedded browser
bool plugins_enabled = gSavedSettings.getBOOL( "BrowserPluginsEnabled" );
media_source->setPluginsEnabled( plugins_enabled );
media_source->setPluginsEnabled( plugins_enabled || clean_browser);
// collect 'javascript enabled' setting from prefs and send to embedded browser
bool javascript_enabled = gSavedSettings.getBOOL( "BrowserJavascriptEnabled" );
media_source->setJavascriptEnabled( javascript_enabled );
media_source->setJavascriptEnabled( javascript_enabled || clean_browser);
bool media_plugin_debugging_enabled = gSavedSettings.getBOOL("MediaPluginDebugging");
media_source->enableMediaPluginDebugging( media_plugin_debugging_enabled );
media_source->enableMediaPluginDebugging( media_plugin_debugging_enabled || clean_browser);
media_source->setTarget(target);
@ -1951,7 +1953,7 @@ bool LLViewerMediaImpl::initializePlugin(const std::string& media_type)
// Save the MIME type that really caused the plugin to load
mCurrentMimeType = mMimeType;
LLPluginClassMedia* media_source = newSourceFromMediaType(mMimeType, this, mMediaWidth, mMediaHeight, mTarget);
LLPluginClassMedia* media_source = newSourceFromMediaType(mMimeType, this, mMediaWidth, mMediaHeight, mTarget, mCleanBrowser);
if (media_source)
{
@ -2575,7 +2577,7 @@ void LLViewerMediaImpl::unload()
}
//////////////////////////////////////////////////////////////////////////////////////////
void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mime_type, bool rediscover_type, bool server_request)
void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mime_type, bool rediscover_type, bool server_request, bool clean_browser)
{
cancelMimeTypeProbe();
@ -2588,6 +2590,7 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi
// Always set the current URL and MIME type.
mMediaURL = url;
mMimeType = mime_type;
mCleanBrowser = clean_browser;
// Clear the current media URL, since it will no longer be correct.
mCurrentMediaURL.clear();

View File

@ -234,7 +234,7 @@ public:
void navigateReload();
void navigateHome();
void unload();
void navigateTo(const std::string& url, const std::string& mime_type = "", bool rediscover_type = false, bool server_request = false);
void navigateTo(const std::string& url, const std::string& mime_type = "", bool rediscover_type = false, bool server_request = false, bool clean_browser = false);
void navigateInternal();
void navigateStop();
bool handleKeyHere(KEY key, MASK mask);
@ -289,7 +289,7 @@ public:
void setTarget(const std::string& target) { mTarget = target; }
// utility function to create a ready-to-use media instance from a desired media type.
static LLPluginClassMedia* newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height, const std::string target = LLStringUtil::null);
static LLPluginClassMedia* newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height, const std::string target = LLStringUtil::null, bool clean_browser = false);
// Internally set our desired browser user agent string, including
// the Second Life version and skin name. Used because we can
@ -464,6 +464,7 @@ private:
bool mTrustedBrowser;
std::string mTarget;
LLNotificationPtr mNotification;
bool mCleanBrowser; // force the creation of a clean browsing target with full options enabled
private:
BOOL mIsUpdated ;

View File

@ -257,7 +257,8 @@ std::string build_extensions_string(LLFilePicker::ELoadFilter filter)
#endif
case LLFilePicker::FFLOAD_XML:
return XML_EXTENSIONS;
case LLFilePicker::FFLOAD_ALL:
case LLFilePicker::FFLOAD_ALL:
case LLFilePicker::FFLOAD_EXE:
return ALL_FILE_EXTENSIONS;
#endif
default:

View File

@ -2862,7 +2862,8 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("EstateChangeInfo");
capabilityNames.append("EventQueueGet");
capabilityNames.append("FacebookConnect");
//capabilityNames.append("FacebookRedirect");
capabilityNames.append("FlickrConnect");
capabilityNames.append("TwitterConnect");
if (gSavedSettings.getBOOL("UseHTTPInventory"))
{

View File

@ -138,6 +138,8 @@ with the same filename but different name
<texture name="Command_Chat_Icon" file_name="toolbar_icons/chat.png" preload="true" />
<texture name="Command_Compass_Icon" file_name="toolbar_icons/land.png" preload="true" />
<texture name="Command_Destinations_Icon" file_name="toolbar_icons/destinations.png" preload="true" />
<texture name="Command_Facebook_Icon" file_name="toolbar_icons/facebook.png" preload="true" />
<texture name="Command_Flickr_Icon" file_name="toolbar_icons/flickr.png" preload="true" />
<texture name="Command_Gestures_Icon" file_name="toolbar_icons/gestures.png" preload="true" />
<texture name="Command_HowTo_Icon" file_name="toolbar_icons/howto.png" preload="true" />
<texture name="Command_Inventory_Icon" file_name="toolbar_icons/inventory.png" preload="true" />
@ -153,9 +155,9 @@ with the same filename but different name
<texture name="Command_Preferences_Icon" file_name="toolbar_icons/preferences.png" preload="true" />
<texture name="Command_Profile_Icon" file_name="toolbar_icons/profile.png" preload="true" />
<texture name="Command_Search_Icon" file_name="toolbar_icons/search.png" preload="true" />
<texture name="Command_Social_Icon" file_name="toolbar_icons/facebook.png" preload="true" />
<texture name="Command_Snapshot_Icon" file_name="toolbar_icons/snapshot.png" preload="true" />
<texture name="Command_Speak_Icon" file_name="toolbar_icons/speak.png" preload="true" />
<texture name="Command_Twitter_Icon" file_name="toolbar_icons/twitter.png" preload="true" />
<texture name="Command_View_Icon" file_name="toolbar_icons/view.png" preload="true" />
<texture name="Command_Voice_Icon" file_name="toolbar_icons/nearbyvoice.png" preload="true" />
<texture name="Caret_Bottom_Icon" file_name="toolbar_icons/caret_bottom.png" preload="true" scale.left="1" scale.top="23" scale.right="15" scale.bottom="1" />

Some files were not shown because too many files have changed in this diff Show More