From 7cc64a09a3ca9211354427206f04d157c9ac30a2 Mon Sep 17 00:00:00 2001
From: Merov Linden <merov@lindenlab.com>
Date: Tue, 14 Jan 2014 21:01:51 -0800
Subject: [PATCH] ACME-1236 : Refactor filters and vignette into llimagefilter,
 add example filters to llimage_libtest

---
 .../llimage_libtest/1970colorize.xml          |  41 ++
 .../llimage_libtest/brighten.xml              |  11 +
 .../llimage_libtest/colorize.xml              |  20 +
 .../llimage_libtest/contrast.xml              |  18 +
 .../llimage_libtest/darken.xml                |  11 +
 .../llimage_libtest/gamma.xml                 |  18 +
 .../llimage_libtest/grayscale.xml             |  14 +
 .../llimage_libtest/horizontalscreen.xml      |  25 +
 .../llimage_libtest/linearize.xml             |  11 +
 .../llimage_libtest/newsscreen.xml            |  25 +
 .../llimage_libtest/posterize.xml             |  18 +
 .../llimage_libtest/rotatecolors180.xml       |   8 +
 .../llimage_libtest/saturate.xml              |   8 +
 .../llimage_libtest/sepia.xml                 |  14 +
 .../llimage_libtest/slantedscreen.xml         |  25 +
 .../llimage_libtest/spotlight.xml             |  45 ++
 .../llimage_libtest/verticalscreen.xml        |  25 +
 .../llimage_libtest/video.xml                 |  23 +
 indra/llimage/llimage.cpp                     | 527 +-------------
 indra/llimage/llimage.h                       |  61 --
 indra/llimage/llimagefilter.cpp               | 641 +++++++++++++++---
 indra/llimage/llimagefilter.h                 |  50 +-
 22 files changed, 956 insertions(+), 683 deletions(-)
 create mode 100644 indra/integration_tests/llimage_libtest/1970colorize.xml
 create mode 100755 indra/integration_tests/llimage_libtest/brighten.xml
 create mode 100644 indra/integration_tests/llimage_libtest/colorize.xml
 create mode 100644 indra/integration_tests/llimage_libtest/contrast.xml
 create mode 100755 indra/integration_tests/llimage_libtest/darken.xml
 create mode 100644 indra/integration_tests/llimage_libtest/gamma.xml
 create mode 100644 indra/integration_tests/llimage_libtest/grayscale.xml
 create mode 100644 indra/integration_tests/llimage_libtest/horizontalscreen.xml
 create mode 100755 indra/integration_tests/llimage_libtest/linearize.xml
 create mode 100755 indra/integration_tests/llimage_libtest/newsscreen.xml
 create mode 100755 indra/integration_tests/llimage_libtest/posterize.xml
 create mode 100644 indra/integration_tests/llimage_libtest/rotatecolors180.xml
 create mode 100644 indra/integration_tests/llimage_libtest/saturate.xml
 create mode 100644 indra/integration_tests/llimage_libtest/sepia.xml
 create mode 100644 indra/integration_tests/llimage_libtest/slantedscreen.xml
 create mode 100644 indra/integration_tests/llimage_libtest/spotlight.xml
 create mode 100644 indra/integration_tests/llimage_libtest/verticalscreen.xml
 create mode 100755 indra/integration_tests/llimage_libtest/video.xml

diff --git a/indra/integration_tests/llimage_libtest/1970colorize.xml b/indra/integration_tests/llimage_libtest/1970colorize.xml
new file mode 100644
index 00000000000..0dab2489a05
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/1970colorize.xml
@@ -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>
diff --git a/indra/integration_tests/llimage_libtest/brighten.xml b/indra/integration_tests/llimage_libtest/brighten.xml
new file mode 100755
index 00000000000..d17b96d2d7a
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/brighten.xml
@@ -0,0 +1,11 @@
+<llsd>
+    <array>
+        <array>
+            <string>brighten</string>
+            <real>50.0</real>
+            <real>1.0</real>
+            <real>1.0</real>
+            <real>1.0</real>
+        </array>
+    </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/colorize.xml b/indra/integration_tests/llimage_libtest/colorize.xml
new file mode 100644
index 00000000000..18c6cd3425c
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/colorize.xml
@@ -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>colorize</string>
+            <real>1.0</real>
+            <real>1.0</real>
+            <real>1.0</real>
+            <real>0.2</real>
+            <real>0.0</real>
+            <real>0.2</real>
+        </array>
+    </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/contrast.xml b/indra/integration_tests/llimage_libtest/contrast.xml
new file mode 100644
index 00000000000..8dcdd1a9a93
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/contrast.xml
@@ -0,0 +1,18 @@
+<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>contrast</string>
+            <real>1.5</real>
+            <real>1.0</real>
+            <real>1.0</real>
+            <real>1.0</real>
+        </array>
+    </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/darken.xml b/indra/integration_tests/llimage_libtest/darken.xml
new file mode 100755
index 00000000000..8d110452e98
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/darken.xml
@@ -0,0 +1,11 @@
+<llsd>
+    <array>
+        <array>
+            <string>darken</string>
+            <real>50.0</real>
+            <real>1.0</real>
+            <real>1.0</real>
+            <real>1.0</real>
+        </array>
+    </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/gamma.xml b/indra/integration_tests/llimage_libtest/gamma.xml
new file mode 100644
index 00000000000..7505a03027f
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/gamma.xml
@@ -0,0 +1,18 @@
+<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>gamma</string>
+            <real>1.5</real>
+            <real>1.0</real>
+            <real>1.0</real>
+            <real>1.0</real>
+        </array>
+    </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/grayscale.xml b/indra/integration_tests/llimage_libtest/grayscale.xml
new file mode 100644
index 00000000000..984312c4fd2
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/grayscale.xml
@@ -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>
diff --git a/indra/integration_tests/llimage_libtest/horizontalscreen.xml b/indra/integration_tests/llimage_libtest/horizontalscreen.xml
new file mode 100644
index 00000000000..ddff4d19771
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/horizontalscreen.xml
@@ -0,0 +1,25 @@
+<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>blend</string>
+            <real>0.0</real>
+            <real>0.0</real>
+        </array>
+        <array>
+            <string>screen</string>
+            <string>line</string>
+            <real>5.0</real>
+            <real>0.0</real>
+        </array>
+    </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/linearize.xml b/indra/integration_tests/llimage_libtest/linearize.xml
new file mode 100755
index 00000000000..23d0290e078
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/linearize.xml
@@ -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>
diff --git a/indra/integration_tests/llimage_libtest/newsscreen.xml b/indra/integration_tests/llimage_libtest/newsscreen.xml
new file mode 100755
index 00000000000..8247c34500a
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/newsscreen.xml
@@ -0,0 +1,25 @@
+<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>blend</string>
+            <real>0.0</real>
+            <real>0.0</real>
+        </array>
+        <array>
+            <string>screen</string>
+            <string>2Dsine</string>
+            <real>5.0</real>
+            <real>0.0</real>
+        </array>
+    </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/posterize.xml b/indra/integration_tests/llimage_libtest/posterize.xml
new file mode 100755
index 00000000000..f026278f9e2
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/posterize.xml
@@ -0,0 +1,18 @@
+<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>posterize</string>
+            <real>10.0</real>
+            <real>1.0</real>
+            <real>1.0</real>
+            <real>1.0</real>
+        </array>
+    </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/rotatecolors180.xml b/indra/integration_tests/llimage_libtest/rotatecolors180.xml
new file mode 100644
index 00000000000..e25029720fe
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/rotatecolors180.xml
@@ -0,0 +1,8 @@
+<llsd>
+    <array>
+        <array>
+            <string>rotate</string>
+            <real>180.0</real>
+        </array>
+    </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/saturate.xml b/indra/integration_tests/llimage_libtest/saturate.xml
new file mode 100644
index 00000000000..b77f07a0374
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/saturate.xml
@@ -0,0 +1,8 @@
+<llsd>
+    <array>
+        <array>
+            <string>saturate</string>
+            <real>3.0</real>
+        </array>
+    </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/sepia.xml b/indra/integration_tests/llimage_libtest/sepia.xml
new file mode 100644
index 00000000000..0304ead015d
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/sepia.xml
@@ -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>
diff --git a/indra/integration_tests/llimage_libtest/slantedscreen.xml b/indra/integration_tests/llimage_libtest/slantedscreen.xml
new file mode 100644
index 00000000000..63ad01d51d5
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/slantedscreen.xml
@@ -0,0 +1,25 @@
+<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>blend</string>
+            <real>0.0</real>
+            <real>0.0</real>
+        </array>
+        <array>
+            <string>screen</string>
+            <string>line</string>
+            <real>5.0</real>
+            <real>45.0</real>
+        </array>
+    </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/spotlight.xml b/indra/integration_tests/llimage_libtest/spotlight.xml
new file mode 100644
index 00000000000..203130bdeed
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/spotlight.xml
@@ -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>
diff --git a/indra/integration_tests/llimage_libtest/verticalscreen.xml b/indra/integration_tests/llimage_libtest/verticalscreen.xml
new file mode 100644
index 00000000000..71e48df6565
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/verticalscreen.xml
@@ -0,0 +1,25 @@
+<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>blend</string>
+            <real>0.0</real>
+            <real>0.0</real>
+        </array>
+        <array>
+            <string>screen</string>
+            <string>line</string>
+            <real>5.0</real>
+            <real>90.0</real>
+        </array>
+    </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/video.xml b/indra/integration_tests/llimage_libtest/video.xml
new file mode 100755
index 00000000000..8b10687ef56
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/video.xml
@@ -0,0 +1,23 @@
+<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>lines</string>
+            <real>10.0</real>
+            <real>0.0</real>
+        </array>
+        <array>
+            <string>brighten</string>
+            <real>100.0</real>
+            <real>1.0</real>
+            <real>1.0</real>
+            <real>1.0</real>
+        </array>
+    </array>
+</llsd>
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index 3105fe37469..7981ca7364c 100755
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -98,14 +98,7 @@ LLImageBase::LLImageBase()
 	  mHeight(0),
 	  mComponents(0),
 	  mBadBufferAllocation(false),
-	  mAllowOverSize(false),
-      mHistoRed(NULL),
-      mHistoGreen(NULL),
-      mHistoBlue(NULL),
-      mHistoBrightness(NULL),
-      mVignetteMode(VIGNETTE_MODE_NONE),
-      mVignetteGamma(1.0),
-      mVignetteMin(0.0)
+	  mAllowOverSize(false)
 {
 }
 
@@ -113,10 +106,6 @@ LLImageBase::LLImageBase()
 LLImageBase::~LLImageBase()
 {
 	deleteData(); // virtual
-    ll_aligned_free_16(mHistoRed);
-    ll_aligned_free_16(mHistoGreen);
-    ll_aligned_free_16(mHistoBlue);
-    ll_aligned_free_16(mHistoBrightness);
 }
 
 //static
@@ -947,520 +936,6 @@ BOOL LLImageRaw::scale( S32 new_width, S32 new_height, BOOL scale_image_data )
 	return TRUE ;
 }
 
-// Filter Operations
-void LLImageRaw::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 LLImageRaw::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 LLImageRaw::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 LLImageRaw::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 LLImageRaw::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,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 LLImageRaw::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 LLImageRaw::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 LLImageRaw::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 LLImageRaw::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 LLImageRaw::filterBrightness(S32 add, const LLColor3& alpha)
-{
-    U8 brightness_red_lut[256];
-    U8 brightness_green_lut[256];
-    U8 brightness_blue_lut[256];
-    
-    for (S32 i = 0; i < 256; i++)
-    {
-        U8 value_i = (U8)(llclampb((S32)((S32)(i) + add)));
-        // 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);
-}
-
-// Filter Primitives
-void LLImageRaw::colorTransform(const LLMatrix3 &transform)
-{
-	const S32 components = getComponents();
-	llassert( components >= 1 && components <= 4 );
-    
-	S32 width  = getWidth();
-    S32 height = getHeight();
-    
-	U8* dst_data = getData();
-	for (S32 j = 0; j < height; j++)
-	{
-        for (S32 i = 0; i < width; i++)
-        {
-            LLVector3 src((F32)(dst_data[VRED]),(F32)(dst_data[VGREEN]),(F32)(dst_data[VBLUE]));
-            LLVector3 dst = src * transform;
-            dst.clamp(0.0f,255.0f);
-            if (mVignetteMode == VIGNETTE_MODE_NONE)
-            {
-                dst_data[VRED]   = dst.mV[VRED];
-                dst_data[VGREEN] = dst.mV[VGREEN];
-                dst_data[VBLUE]  = dst.mV[VBLUE];
-            }
-            else
-            {
-                F32 alpha = getVignetteAlpha(i,j);
-                if (mVignetteMode == VIGNETTE_MODE_BLEND)
-                {
-                    // Blends with the source image on the edges
-                    F32 inv_alpha = 1.0 - alpha;
-                    dst_data[VRED]   = inv_alpha * src.mV[VRED]   + alpha * dst.mV[VRED];
-                    dst_data[VGREEN] = inv_alpha * src.mV[VGREEN] + alpha * dst.mV[VGREEN];
-                    dst_data[VBLUE]  = inv_alpha * src.mV[VBLUE]  + alpha * dst.mV[VBLUE];
-                }
-                else // VIGNETTE_MODE_FADE
-                {
-                    // Fade to black on the edges
-                    dst_data[VRED]   = alpha * dst.mV[VRED];
-                    dst_data[VGREEN] = alpha * dst.mV[VGREEN];
-                    dst_data[VBLUE]  = alpha * dst.mV[VBLUE];
-                }
-            }
-            dst_data += components;
-        }
-	}
-}
-
-void LLImageRaw::colorCorrect(const U8* lut_red, const U8* lut_green, const U8* lut_blue)
-{
-	const S32 components = getComponents();
-	llassert( components >= 1 && components <= 4 );
-    
-	S32 width  = getWidth();
-    S32 height = getHeight();
-    
-	U8* dst_data = getData();
-	for (S32 j = 0; j < height; j++)
-	{
-        for (S32 i = 0; i < width; i++)
-        {
-            if (mVignetteMode == VIGNETTE_MODE_NONE)
-            {
-                dst_data[VRED]   = lut_red[dst_data[VRED]];
-                dst_data[VGREEN] = lut_green[dst_data[VGREEN]];
-                dst_data[VBLUE]  = lut_blue[dst_data[VBLUE]];
-            }
-            else
-            {
-                F32 alpha = getVignetteAlpha(i,j);
-                if (mVignetteMode == VIGNETTE_MODE_BLEND)
-                {
-                    // Blends with the source image on the edges
-                    F32 inv_alpha = 1.0 - alpha;
-                    dst_data[VRED]   = inv_alpha * dst_data[VRED]  + alpha * lut_red[dst_data[VRED]];
-                    dst_data[VGREEN] = inv_alpha * dst_data[VGREEN] + alpha * lut_green[dst_data[VGREEN]];
-                    dst_data[VBLUE]  = inv_alpha * dst_data[VBLUE]  + alpha * lut_blue[dst_data[VBLUE]];
-                }
-                else // VIGNETTE_MODE_FADE
-                {
-                    // Fade to black on the edges
-                    dst_data[VRED]   = alpha * lut_red[dst_data[VRED]];
-                    dst_data[VGREEN] = alpha * lut_green[dst_data[VGREEN]];
-                    dst_data[VBLUE]  = alpha * lut_blue[dst_data[VBLUE]];
-                }
-            }
-            dst_data += components;
-        }
-	}
-}
-
-void LLImageRaw::filterScreen(EScreenMode mode, const S32 wave_length, const F32 angle)
-{
-	const S32 components = getComponents();
-	llassert( components >= 1 && components <= 4 );
-    
-	S32 width  = getWidth();
-    S32 height = getHeight();
-    
-    F32 sin = sinf(angle*DEG_TO_RAD);
-    F32 cos = cosf(angle*DEG_TO_RAD);
-    
-	U8* dst_data = getData();
-	for (S32 j = 0; j < height; j++)
-	{
-        for (S32 i = 0; i < width; i++)
-        {
-            F32 value = 0.0;
-            F32 d = 0.0;
-            switch (mode)
-            {
-                case SCREEN_MODE_2DSINE:
-                    value = (sinf(2*F_PI*i/wave_length)*sinf(2*F_PI*j/wave_length)+1.0)*255.0/2.0;
-                    break;
-                case SCREEN_MODE_LINE:
-                    d = sin*i - cos*j;
-                    value = (sinf(2*F_PI*d/wave_length)+1.0)*255.0/2.0;
-                    break;
-            }
-            U8 dst_value = (dst_data[VRED] >= (U8)(value) ? 255 : 0);
-            
-            if (mVignetteMode == VIGNETTE_MODE_NONE)
-            {
-                dst_data[VRED]   = dst_value;
-                dst_data[VGREEN] = dst_value;
-                dst_data[VBLUE]  = dst_value;
-            }
-            else
-            {
-                F32 alpha = getVignetteAlpha(i,j);
-                if (mVignetteMode == VIGNETTE_MODE_BLEND)
-                {
-                    // Blends with the source image on the edges
-                    F32 inv_alpha = 1.0 - alpha;
-                    dst_data[VRED]   = inv_alpha * dst_data[VRED]   + alpha * dst_value;
-                    dst_data[VGREEN] = inv_alpha * dst_data[VGREEN] + alpha * dst_value;
-                    dst_data[VBLUE]  = inv_alpha * dst_data[VBLUE]  + alpha * dst_value;
-                }
-                else // VIGNETTE_MODE_FADE
-                {
-                    // Fade to black on the edges
-                    dst_data[VRED]   = alpha * dst_value;
-                    dst_data[VGREEN] = alpha * dst_value;
-                    dst_data[VBLUE]  = alpha * dst_value;
-                }
-            }
-            dst_data += components;
-        }
-	}
-}
-
-void LLImageRaw::setVignette(EVignetteMode mode, EVignetteType type, F32 gamma, F32 min)
-{
-    mVignetteMode = mode;
-    mVignetteType = type;
-    mVignetteGamma = gamma;
-    mVignetteMin = llclampf(min);
-    // We always center the vignette on the image and fits it in the image smallest dimension
-    mVignetteCenterX = getWidth()/2;
-    mVignetteCenterY = getHeight()/2;
-    mVignetteWidth = llmin(getWidth()/2,getHeight()/2);
-}
-
-F32 LLImageRaw::getVignetteAlpha(S32 i, S32 j)
-{
-    F32 alpha = 1.0;
-    if (mVignetteType == VIGNETTE_TYPE_CENTER)
-    {
-        // 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 - mVignetteCenterX)*(i - mVignetteCenterX) + (j - mVignetteCenterY)*(j - mVignetteCenterY);
-        alpha = powf(F_E, -(powf((d_center_square/(mVignetteWidth*mVignetteWidth)),mVignetteGamma)/2.0f));
-    }
-    else if (mVignetteType == VIGNETTE_TYPE_LINES)
-    {
-        // alpha varies according to a squared sine function vertically.
-        // gamma is interpreted as the wavelength (in pixels) of the sine in that case.
-        alpha = (sinf(2*F_PI*j/mVignetteGamma) > 0.0 ? 1.0 : 0.0);
-    }
-    // We rescale alpha between min and 1.0 so to avoid complete fading if so desired.
-    return (mVignetteMin + alpha * (1.0 - mVignetteMin));
-}
-
-U32* LLImageRaw::getBrightnessHistogram()
-{
-    if (!mHistoBrightness)
-    {
-        computeHistograms();
-    }
-    return mHistoBrightness;
-}
-
-void LLImageRaw::computeHistograms()
-{
- 	const S32 components = 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 = getWidth() * getHeight();
-	U8* dst_data = 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;
-	}
-}
-
 void LLImageRaw::copyLineScaled( U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len, S32 in_pixel_step, S32 out_pixel_step )
 {
 	const S32 components = getComponents();
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index a600f2e4a6a..4b1da233e1d 100755
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -88,25 +88,6 @@ typedef enum e_image_codec
 	IMG_CODEC_EOF  = 8
 } EImageCodec;
 
-typedef enum e_vignette_mode
-{
-	VIGNETTE_MODE_NONE  = 0,
-	VIGNETTE_MODE_BLEND = 1,
-	VIGNETTE_MODE_FADE  = 2
-} EVignetteMode;
-
-typedef enum e_vignette_type
-{
-	VIGNETTE_TYPE_CENTER = 0,
-	VIGNETTE_TYPE_LINES  = 1
-} EVignetteType;
-
-typedef enum e_screen_mode
-{
-	SCREEN_MODE_2DSINE   = 0,
-	SCREEN_MODE_LINE     = 1
-} EScreenMode;
-
 //============================================================================
 // library initialization class
 
@@ -171,21 +152,6 @@ class LLImageBase : public LLThreadSafeRefCount
 	// special accessor to allow direct setting of mData and mDataSize by LLImageFormatted
 	void setDataAndSize(U8 *data, S32 size);
 	
-    // Histograms (if we ever happen to need them)
-    U32 *mHistoRed;
-    U32 *mHistoGreen;
-    U32 *mHistoBlue;
-    U32 *mHistoBrightness;
-    
-    // Vignette filtering
-    EVignetteMode mVignetteMode;
-    EVignetteType mVignetteType;
-    S32 mVignetteCenterX;
-    S32 mVignetteCenterY;
-    S32 mVignetteWidth;
-    F32 mVignetteGamma;
-    F32 mVignetteMin;
-    
 public:
 	static void generateMip(const U8 *indata, U8* mipdata, int width, int height, S32 nchannels);
 	
@@ -291,30 +257,6 @@ class LLImageRaw : public LLImageBase
 
 	// Src and dst are same size.  Src has 4 components.  Dst has 3 components.
 	void compositeUnscaled4onto3( LLImageRaw* src );
-    
-    // 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(S32 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 S32 wave_length, const F32 angle);
-    void setVignette(EVignetteMode mode, EVignetteType type, F32 gamma, F32 min);
-    U32* getBrightnessHistogram();
 
 protected:
 	// Create an image from a local file (generally used in tools)
@@ -327,9 +269,6 @@ class LLImageRaw : public LLImageBase
 
 	void setDataAndSize(U8 *data, S32 width, S32 height, S8 components) ;
 
-    void computeHistograms();
-    F32 getVignetteAlpha(S32 i, S32 j);
-
 public:
 	static S32 sGlobalRawMemory;
 	static S32 sRawImageCount;
diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp
index e2d281e48d5..ed97d9ae17c 100755
--- a/indra/llimage/llimagefilter.cpp
+++ b/indra/llimage/llimagefilter.cpp
@@ -40,16 +40,28 @@
 //---------------------------------------------------------------------------
 
 LLImageFilter::LLImageFilter() :
-    mFilterData(LLSD::emptyArray())
+    mFilterData(LLSD::emptyArray()),
+    mImage(NULL),
+    mHistoRed(NULL),
+    mHistoGreen(NULL),
+    mHistoBlue(NULL),
+    mHistoBrightness(NULL),
+    mVignetteMode(VIGNETTE_MODE_NONE),
+    mVignetteGamma(1.0),
+    mVignetteMin(0.0)
 {
 }
 
 LLImageFilter::~LLImageFilter()
 {
+    mImage = NULL;
+    ll_aligned_free_16(mHistoRed);
+    ll_aligned_free_16(mHistoGreen);
+    ll_aligned_free_16(mHistoBlue);
+    ll_aligned_free_16(mHistoBrightness);
 }
 
 /*
- " -f, --filter <name> [<param>]\n"
  "        Apply the filter <name> to the input images using the optional <param> value. Admissible names:\n"
  "        - 'grayscale' converts to grayscale (no param).\n"
  "        - 'sepia' converts to sepia (no param).\n"
@@ -67,86 +79,10 @@ LLImageFilter::~LLImageFilter()
  "        - 'verticalscreen' applies a vertical screening to the red channel and output to black and white.\n"
  "        - 'slantedscreen' applies a 45 degrees slanted screening to the red channel and output to black and white.\n"
  "        - Any other value will be interpreted as a file name describing a sequence of filters and parameters to be applied to the input images.\n"
- " -v, --vignette <name> [<feather> <min>]\n"
+
  "        Apply a circular central vignette <name> to the filter using the optional <feather> and <min> values. Admissible names:\n"
  "        - 'blend' : the filter is applied with full intensity in the center and blends with the image to the periphery.\n"
  "        - 'fade' : the filter is applied with full intensity in the center and fades to black to the periphery.\n"
-
- // Set the vignette if any
- if (vignette_name == "blend")
- {
- raw_image->setVignette(VIGNETTE_MODE_BLEND,VIGNETTE_TYPE_CENTER,(float)(vignette_param_1),(float)(vignette_param_2));
- }
- else if (vignette_name == "fade")
- {
- raw_image->setVignette(VIGNETTE_MODE_FADE,VIGNETTE_TYPE_CENTER,(float)(vignette_param_1),(float)(vignette_param_2));
- }
- 
- // Apply filter if any
- if (filter_name == "sepia")
- {
- raw_image->filterSepia();
- }
- else if (filter_name == "grayscale")
- {
- raw_image->filterGrayScale();
- }
- else if (filter_name == "saturate")
- {
- raw_image->filterSaturate((float)(filter_param));
- }
- else if (filter_name == "rotate")
- {
- raw_image->filterRotate((float)(filter_param));
- }
- else if (filter_name == "gamma")
- {
- raw_image->filterGamma((float)(filter_param),LLColor3::white);
- }
- else if (filter_name == "colorize")
- {
- // For testing, we just colorize in the red channel, modulate by the alpha passed as a parameter
- LLColor3 color(1.0,0.0,0.0);
- LLColor3 alpha((F32)(filter_param),0.0,0.0);
- raw_image->filterColorize(color,alpha);
- }
- else if (filter_name == "contrast")
- {
- raw_image->filterContrast((float)(filter_param),LLColor3::white);
- }
- else if (filter_name == "brighten")
- {
- raw_image->filterBrightness((S32)(filter_param),LLColor3::white);
- }
- else if (filter_name == "darken")
- {
- raw_image->filterBrightness((S32)(-filter_param),LLColor3::white);
- }
- else if (filter_name == "linearize")
- {
- raw_image->filterLinearize((float)(filter_param),LLColor3::white);
- }
- else if (filter_name == "posterize")
- {
- raw_image->filterEqualize((S32)(filter_param),LLColor3::white);
- }
- else if (filter_name == "newsscreen")
- {
- raw_image->filterScreen(SCREEN_MODE_2DSINE,(S32)(filter_param),0.0);
- }
- else if (filter_name == "horizontalscreen")
- {
- raw_image->filterScreen(SCREEN_MODE_LINE,(S32)(filter_param),0.0);
- }
- else if (filter_name == "verticalscreen")
- {
- raw_image->filterScreen(SCREEN_MODE_LINE,(S32)(filter_param),90.0);
- }
- else if (filter_name == "slantedscreen")
- {
- raw_image->filterScreen(SCREEN_MODE_LINE,(S32)(filter_param),45.0);
- }
- 
  */
 
 // Load filter from file
@@ -171,6 +107,8 @@ void LLImageFilter::loadFromFile(const std::string& file_path)
 // 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)
 	{
@@ -186,67 +124,67 @@ void LLImageFilter::executeFilter(LLPointer<LLImageRaw> raw_image)
         // Execute the filter described on this line
         if (filter_name == "blend")
         {
-            raw_image->setVignette(VIGNETTE_MODE_BLEND,VIGNETTE_TYPE_CENTER,(float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal()));
+            setVignette(VIGNETTE_MODE_BLEND,VIGNETTE_TYPE_CENTER,(float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal()));
         }
         else if (filter_name == "fade")
         {
-            raw_image->setVignette(VIGNETTE_MODE_FADE,VIGNETTE_TYPE_CENTER,(float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal()));
+            setVignette(VIGNETTE_MODE_FADE,VIGNETTE_TYPE_CENTER,(float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal()));
         }
         else if (filter_name == "lines")
         {
-            raw_image->setVignette(VIGNETTE_MODE_BLEND,VIGNETTE_TYPE_LINES,(float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal()));
+            setVignette(VIGNETTE_MODE_BLEND,VIGNETTE_TYPE_LINES,(float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal()));
         }
         else if (filter_name == "sepia")
         {
-            raw_image->filterSepia();
+            filterSepia();
         }
         else if (filter_name == "grayscale")
         {
-            raw_image->filterGrayScale();
+            filterGrayScale();
         }
         else if (filter_name == "saturate")
         {
-            raw_image->filterSaturate((float)(mFilterData[i][1].asReal()));
+            filterSaturate((float)(mFilterData[i][1].asReal()));
         }
         else if (filter_name == "rotate")
         {
-            raw_image->filterRotate((float)(mFilterData[i][1].asReal()));
+            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()));
-            raw_image->filterGamma((float)(mFilterData[i][1].asReal()),color);
+            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()));
-            raw_image->filterColorize(color,alpha);
+            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()));
-            raw_image->filterContrast((float)(mFilterData[i][1].asReal()),color);
+            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()));
-            raw_image->filterBrightness((S32)(mFilterData[i][1].asReal()),color);
+            filterBrightness((S32)(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()));
-            raw_image->filterBrightness((S32)(-mFilterData[i][1].asReal()),color);
+            filterBrightness((S32)(-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()));
-            raw_image->filterLinearize((float)(mFilterData[i][1].asReal()),color);
+            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()));
-            raw_image->filterEqualize((S32)(mFilterData[i][1].asReal()),color);
+            filterEqualize((S32)(mFilterData[i][1].asReal()),color);
         }
         else if (filter_name == "screen")
         {
@@ -260,10 +198,525 @@ void LLImageFilter::executeFilter(LLPointer<LLImageRaw> raw_image)
             {
                 mode = SCREEN_MODE_LINE;
             }
-            raw_image->filterScreen(mode,(S32)(mFilterData[i][2].asReal()),(F32)(mFilterData[i][3].asReal()));
+            filterScreen(mode,(S32)(mFilterData[i][2].asReal()),(F32)(mFilterData[i][3].asReal()));
         }
     }
 }
 
+// Filter Primitives
+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++)
+        {
+            if (mVignetteMode == VIGNETTE_MODE_NONE)
+            {
+                dst_data[VRED]   = lut_red[dst_data[VRED]];
+                dst_data[VGREEN] = lut_green[dst_data[VGREEN]];
+                dst_data[VBLUE]  = lut_blue[dst_data[VBLUE]];
+            }
+            else
+            {
+                F32 alpha = getVignetteAlpha(i,j);
+                if (mVignetteMode == VIGNETTE_MODE_BLEND)
+                {
+                    // Blends with the source image on the edges
+                    F32 inv_alpha = 1.0 - alpha;
+                    dst_data[VRED]   = inv_alpha * dst_data[VRED]  + alpha * lut_red[dst_data[VRED]];
+                    dst_data[VGREEN] = inv_alpha * dst_data[VGREEN] + alpha * lut_green[dst_data[VGREEN]];
+                    dst_data[VBLUE]  = inv_alpha * dst_data[VBLUE]  + alpha * lut_blue[dst_data[VBLUE]];
+                }
+                else // VIGNETTE_MODE_FADE
+                {
+                    // Fade to black on the edges
+                    dst_data[VRED]   = alpha * lut_red[dst_data[VRED]];
+                    dst_data[VGREEN] = alpha * lut_green[dst_data[VGREEN]];
+                    dst_data[VBLUE]  = alpha * 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++)
+        {
+            LLVector3 src((F32)(dst_data[VRED]),(F32)(dst_data[VGREEN]),(F32)(dst_data[VBLUE]));
+            LLVector3 dst = src * transform;
+            dst.clamp(0.0f,255.0f);
+            if (mVignetteMode == VIGNETTE_MODE_NONE)
+            {
+                dst_data[VRED]   = dst.mV[VRED];
+                dst_data[VGREEN] = dst.mV[VGREEN];
+                dst_data[VBLUE]  = dst.mV[VBLUE];
+            }
+            else
+            {
+                F32 alpha = getVignetteAlpha(i,j);
+                if (mVignetteMode == VIGNETTE_MODE_BLEND)
+                {
+                    // Blends with the source image on the edges
+                    F32 inv_alpha = 1.0 - alpha;
+                    dst_data[VRED]   = inv_alpha * src.mV[VRED]   + alpha * dst.mV[VRED];
+                    dst_data[VGREEN] = inv_alpha * src.mV[VGREEN] + alpha * dst.mV[VGREEN];
+                    dst_data[VBLUE]  = inv_alpha * src.mV[VBLUE]  + alpha * dst.mV[VBLUE];
+                }
+                else // VIGNETTE_MODE_FADE
+                {
+                    // Fade to black on the edges
+                    dst_data[VRED]   = alpha * dst.mV[VRED];
+                    dst_data[VGREEN] = alpha * dst.mV[VGREEN];
+                    dst_data[VBLUE]  = alpha * dst.mV[VBLUE];
+                }
+            }
+            dst_data += components;
+        }
+	}
+}
+
+void LLImageFilter::filterScreen(EScreenMode mode, const S32 wave_length, const F32 angle)
+{
+	const S32 components = mImage->getComponents();
+	llassert( components >= 1 && components <= 4 );
+    
+	S32 width  = mImage->getWidth();
+    S32 height = mImage->getHeight();
+    
+    F32 sin = sinf(angle*DEG_TO_RAD);
+    F32 cos = cosf(angle*DEG_TO_RAD);
+    
+	U8* dst_data = mImage->getData();
+	for (S32 j = 0; j < height; j++)
+	{
+        for (S32 i = 0; i < width; i++)
+        {
+            F32 value = 0.0;
+            F32 d = 0.0;
+            switch (mode)
+            {
+                case SCREEN_MODE_2DSINE:
+                    value = (sinf(2*F_PI*i/wave_length)*sinf(2*F_PI*j/wave_length)+1.0)*255.0/2.0;
+                    break;
+                case SCREEN_MODE_LINE:
+                    d = sin*i - cos*j;
+                    value = (sinf(2*F_PI*d/wave_length)+1.0)*255.0/2.0;
+                    break;
+            }
+            U8 dst_value = (dst_data[VRED] >= (U8)(value) ? 255 : 0);
+            
+            if (mVignetteMode == VIGNETTE_MODE_NONE)
+            {
+                dst_data[VRED]   = dst_value;
+                dst_data[VGREEN] = dst_value;
+                dst_data[VBLUE]  = dst_value;
+            }
+            else
+            {
+                F32 alpha = getVignetteAlpha(i,j);
+                if (mVignetteMode == VIGNETTE_MODE_BLEND)
+                {
+                    // Blends with the source image on the edges
+                    F32 inv_alpha = 1.0 - alpha;
+                    dst_data[VRED]   = inv_alpha * dst_data[VRED]   + alpha * dst_value;
+                    dst_data[VGREEN] = inv_alpha * dst_data[VGREEN] + alpha * dst_value;
+                    dst_data[VBLUE]  = inv_alpha * dst_data[VBLUE]  + alpha * dst_value;
+                }
+                else // VIGNETTE_MODE_FADE
+                {
+                    // Fade to black on the edges
+                    dst_data[VRED]   = alpha * dst_value;
+                    dst_data[VGREEN] = alpha * dst_value;
+                    dst_data[VBLUE]  = alpha * dst_value;
+                }
+            }
+            dst_data += components;
+        }
+	}
+}
+
+// Procedural Stencils
+void LLImageFilter::setVignette(EVignetteMode mode, EVignetteType type, F32 gamma, F32 min)
+{
+    mVignetteMode = mode;
+    mVignetteType = type;
+    mVignetteGamma = gamma;
+    mVignetteMin = llclampf(min);
+    // We always center the vignette on the image and fits it in the image smallest dimension
+    mVignetteCenterX = mImage->getWidth()/2;
+    mVignetteCenterY = mImage->getHeight()/2;
+    mVignetteWidth = llmin(mImage->getWidth()/2,mImage->getHeight()/2);
+}
+
+F32 LLImageFilter::getVignetteAlpha(S32 i, S32 j)
+{
+    F32 alpha = 1.0;
+    if (mVignetteType == VIGNETTE_TYPE_CENTER)
+    {
+        // 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 - mVignetteCenterX)*(i - mVignetteCenterX) + (j - mVignetteCenterY)*(j - mVignetteCenterY);
+        alpha = powf(F_E, -(powf((d_center_square/(mVignetteWidth*mVignetteWidth)),mVignetteGamma)/2.0f));
+    }
+    else if (mVignetteType == VIGNETTE_TYPE_LINES)
+    {
+        // alpha varies according to a squared sine function vertically.
+        // gamma is interpreted as the wavelength (in pixels) of the sine in that case.
+        alpha = (sinf(2*F_PI*j/mVignetteGamma) > 0.0 ? 1.0 : 0.0);
+    }
+    // We rescale alpha between min and 1.0 so to avoid complete fading if so desired.
+    return (mVignetteMin + alpha * (1.0 - mVignetteMin));
+}
+
+// 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,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(S32 add, const LLColor3& alpha)
+{
+    U8 brightness_red_lut[256];
+    U8 brightness_green_lut[256];
+    U8 brightness_blue_lut[256];
+    
+    for (S32 i = 0; i < 256; i++)
+    {
+        U8 value_i = (U8)(llclampb((S32)((S32)(i) + add)));
+        // 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);
+}
 
 //============================================================================
diff --git a/indra/llimage/llimagefilter.h b/indra/llimage/llimagefilter.h
index 5598fa69f5c..c67789ede0a 100755
--- a/indra/llimage/llimagefilter.h
+++ b/indra/llimage/llimagefilter.h
@@ -29,7 +29,6 @@
 
 #include "llimage.h"
 
-/*
 class LLImageRaw;
 class LLColor4U;
 class LLColor3;
@@ -53,7 +52,7 @@ typedef enum e_screen_mode
 	SCREEN_MODE_2DSINE   = 0,
 	SCREEN_MODE_LINE     = 1
 } EScreenMode;
-*/
+
 //============================================================================
 // Image Filter 
 
@@ -65,8 +64,55 @@ class LLImageFilter
     
     void loadFromFile(const std::string& file_path);
     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(S32 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 S32 wave_length, const F32 angle);
+
+    // Procedural Stencils
+    void setVignette(EVignetteMode mode, EVignetteType type, F32 gamma, F32 min);
+    F32 getVignetteAlpha(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;
+    
+    // Vignette filtering
+    EVignetteMode mVignetteMode;
+    EVignetteType mVignetteType;
+    S32 mVignetteCenterX;
+    S32 mVignetteCenterY;
+    S32 mVignetteWidth;
+    F32 mVignetteGamma;
+    F32 mVignetteMin;
 };
 
 
-- 
GitLab