diff --git a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
index 034c816742b23090990c6446ba125117ae9ca6b0..e485136f588a2273c6f81d39109607dfee3a39d8 100755
--- a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
+++ b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
@@ -83,6 +83,9 @@ 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 <name>\n"
+"        Apply the filter <name> to the input images.\n"
+"        Note: so far, only grayscale and sepia are supported.\n"
 " -log, --logmetrics <metric>\n"
 "        Log performance data for <metric>. Results in <metric>.slp\n"
 "        Note: so far, only ImageCompressionTester has been tested.\n"
@@ -350,6 +353,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,6 +527,26 @@ int main(int argc, char** argv)
 					break;
 			}
 		}
+		else if (!strcmp(argv[arg], "--filter") || !strcmp(argv[arg], "-f"))
+		{
+			// '--filter' needs to be specified with a named filter argument
+			// Note: for the moment, only sepia and grayscale are supported
+			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;
@@ -568,6 +592,16 @@ int main(int argc, char** argv)
 			std::cout << "Error: Image " << *in_file << " could not be loaded" << std::endl;
 			continue;
 		}
+        
+        // Apply filter if any
+        if (filter_name == "sepia")
+        {
+            raw_image->filterSepia();
+        }
+        else if (filter_name == "grayscale")
+        {
+            raw_image->filterGrayScale();
+        }
 	
 		// Save file
 		if (out_file != out_end)
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index e5281feff02e21a9e5b299b22fb148474e4c591b..73e6f48a8defd6a90020b5abd9121988cdad6063 100755
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -30,6 +30,8 @@
 
 #include "llmath.h"
 #include "v4coloru.h"
+#include "m3math.h"
+#include "v3math.h"
 
 #include "llimagebmp.h"
 #include "llimagetga.h"
@@ -933,22 +935,42 @@ BOOL LLImageRaw::scale( S32 new_width, S32 new_height, BOOL scale_image_data )
 	return TRUE ;
 }
 
-// *TODO : Implement real color transform
-// Merov : This is temporary code for testing... 
-void LLImageRaw::colorTransform()
+// 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);
+}
+
+// Filter Primitives
+void LLImageRaw::colorTransform(const LLMatrix3 &transform)
 {
 	const S32 components = getComponents();
 	llassert( components >= 1 && components <= 4 );
     
 	S32 pixels = getWidth() * getHeight();
 	U8* dst_data = getData();
-    llinfos << "Merov : Convert the image to Black and White!!! pixels = " << pixels << ", comp = " << components << llendl;
 	for( S32 i=0; i<pixels; i++ )
 	{
-        U8 gray = (U8)(((U32)(dst_data[0]) + (U32)(dst_data[1]) + (U32)(dst_data[2]))/3);
-		dst_data[0] = gray;
-		dst_data[1] = gray;
-		dst_data[2] = gray;
+        LLVector3 src((F32)(dst_data[VRED]),(F32)(dst_data[VGREEN]),(F32)(dst_data[VBLUE]));
+        LLVector3 dst = src * transform;
+        dst.clamp(0.0f,255.0f);
+		dst_data[VRED]   = dst.mV[VRED];
+		dst_data[VGREEN] = dst.mV[VGREEN];
+		dst_data[VBLUE]  = dst.mV[VBLUE];
 		dst_data += components;
 	}
 }
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index 96c37f5436ac08fdc839d39e408af8ffc6ad6d9f..d6b7e65c7689ebb539059a7f414528426c89dbca 100755
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -71,6 +71,7 @@ const S32 HTTP_PACKET_SIZE = 1496;
 class LLImageFormatted;
 class LLImageRaw;
 class LLColor4U;
+class LLMatrix3;
 class LLPrivateMemoryPool;
 
 typedef enum e_image_codec
@@ -256,8 +257,11 @@ class LLImageRaw : public LLImageBase
 	// Src and dst are same size.  Src has 4 components.  Dst has 3 components.
 	void compositeUnscaled4onto3( LLImageRaw* src );
     
-    // Filter operations
-    void colorTransform();
+    // Filter Operations
+    void filterGrayScale();
+    void filterSepia();
+    // Filter Primitives
+    void colorTransform(const LLMatrix3 &transform);
 
 protected:
 	// Create an image from a local file (generally used in tools)
diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp
index ab9788a88b433667e1b44f6971949d30df9ff775..2931178ace02fabf32754d815088affbd82702b2 100644
--- a/indra/newview/llsnapshotlivepreview.cpp
+++ b/indra/newview/llsnapshotlivepreview.cpp
@@ -589,7 +589,7 @@ void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update)
         // Merov : Filter also the thumbnail?
         if (getFilter() == 1)
         {
-            raw->colorTransform();
+            raw->filterGrayScale();
         }
 		mThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE);
 		mThumbnailUpToDate = TRUE ;
@@ -698,7 +698,7 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview )
             // Merov : Time to apply the filter to mPreviewImage!!!
             if (previewp->getFilter() == 1)
             {
-                previewp->mPreviewImage->colorTransform();
+                previewp->mPreviewImage->filterGrayScale();
             }
             
 			// delete any existing image