From 1b5fb662927ac84606cf767c8c9ec350e82b656f Mon Sep 17 00:00:00 2001
From: Merov Linden <merov@lindenlab.com>
Date: Fri, 3 Jan 2014 14:19:29 -0800
Subject: [PATCH] ACME-1236 : WIP : Added the vignette mode. Can be applied to
 colorCorrect and color Transform. Added new -v argument to llimage_libtest

---
 .../llimage_libtest/llimage_libtest.cpp       |  47 +++++++-
 indra/llimage/llimage.cpp                     | 106 +++++++++++++++---
 indra/llimage/llimage.h                       |  16 +++
 3 files changed, 152 insertions(+), 17 deletions(-)

diff --git a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
index 4d32282a0d9..45e60f64e66 100755
--- a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
+++ b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
@@ -97,6 +97,10 @@ static const char USAGE[] = "\n"
 "        - 'darken' substracts <param> light to the image (<param> between 0 and 255).\n"
 "        - 'linearize' optimizes the contrast using the brightness histogram. <param> is the fraction (between 0.0 and 1.0) of discarded tail of the histogram.\n"
 "        - 'posterize' redistributes the colors between <param> classes per channel (<param> between 2 and 255).\n"
+" -v, --vignette <name> [<feather>]\n"
+"        Apply a circular central vignette <name> to the filter using the optional <feather> value. 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"
 " -log, --logmetrics <metric>\n"
 "        Log performance data for <metric>. Results in <metric>.slp\n"
 "        Note: so far, only ImageCompressionTester has been tested.\n"
@@ -366,6 +370,8 @@ int main(int argc, char** argv)
 	bool reversible = false;
     std::string filter_name = "";
     double filter_param = 0.0;
+    std::string vignette_name = "";
+    double vignette_param = 1.0;
 
 	// Init whatever is necessary
 	ll_init_apr();
@@ -568,7 +574,36 @@ int main(int argc, char** argv)
                }
             }
 		}
-		else if (!strcmp(argv[arg], "--analyzeperformance") || !strcmp(argv[arg], "-a"))
+		else if (!strcmp(argv[arg], "--vignette") || !strcmp(argv[arg], "-v"))
+		{
+			// '--vignette' needs to be specified with a named vignette argument
+			if ((arg + 1) < argc)
+			{
+				vignette_name = argv[arg+1];
+			}
+			if (((arg + 1) >= argc) || (vignette_name[0] == '-'))
+			{
+				// We don't have an argument left in the arg list or the next argument is another option
+				std::cout << "No --vignette argument given, no vignette will be applied to filters" << std::endl;
+			}
+			else
+			{
+				arg += 1;					// Skip that arg now we know it's a valid vignette name
+				if ((arg + 1) == argc)		// Break out of the loop if we reach the end of the arg list
+					break;
+                // --vignette can also have an optional parameter
+                std::string value_str;
+                value_str = argv[arg+1];    // Check the next arg
+                if (value_str[0] != '-')    // If it's not another argument, it's a vignette parameter value
+                {
+                    vignette_param = atof(value_str.c_str());
+                    arg += 1;					// Skip that arg now we used it as a valid vignette param
+                    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;
 		}
@@ -614,6 +649,16 @@ int main(int argc, char** argv)
 			continue;
 		}
         
+        // Set the vignette if any
+        if (vignette_name == "blend")
+        {
+            raw_image->setVignette(VIGNETTE_MODE_BLEND,(float)(vignette_param));
+        }
+        else if (vignette_name == "fade")
+        {
+            raw_image->setVignette(VIGNETTE_MODE_FADE,(float)(vignette_param));
+        }
+        
         // Apply filter if any
         if (filter_name == "sepia")
         {
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index 5dc9c24f6ce..da22ed5e5b1 100755
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -101,7 +101,9 @@ LLImageBase::LLImageBase()
       mHistoRed(NULL),
       mHistoGreen(NULL),
       mHistoBlue(NULL),
-      mHistoBrightness(NULL)
+      mHistoBrightness(NULL),
+      mVignetteMode(VIGNETTE_MODE_NONE),
+      mVignetteGamma(1.0)
 {
 }
 
@@ -1194,17 +1196,44 @@ void LLImageRaw::colorTransform(const LLMatrix3 &transform)
 	const S32 components = getComponents();
 	llassert( components >= 1 && components <= 4 );
     
-	S32 pixels = getWidth() * getHeight();
+	S32 width  = getWidth();
+    S32 height = getHeight();
+    
 	U8* dst_data = getData();
-	for (S32 i = 0; i < pixels; i++)
+	for (S32 j = 0; j < height; j++)
 	{
-        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;
+        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;
+        }
 	}
 }
 
@@ -1213,17 +1242,62 @@ void LLImageRaw::colorCorrect(const U8* lut_red, const U8* lut_green, const U8*
 	const S32 components = getComponents();
 	llassert( components >= 1 && components <= 4 );
     
-	S32 pixels = getWidth() * getHeight();
+	S32 width  = getWidth();
+    S32 height = getHeight();
+    
 	U8* dst_data = getData();
-	for (S32 i = 0; i < pixels; i++)
+	for (S32 j = 0; j < height; j++)
 	{
-		dst_data[VRED]   = lut_red[dst_data[VRED]];
-		dst_data[VGREEN] = lut_green[dst_data[VGREEN]];
-		dst_data[VBLUE]  = lut_blue[dst_data[VBLUE]];
-		dst_data += components;
+        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::setVignette(EVignetteMode mode, F32 gamma)
+{
+    mVignetteMode = mode;
+    mVignetteGamma = gamma;
+    // 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)
+{
+    // 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 to 0.0
+    F32 d_center_square = (i - mVignetteCenterX)*(i - mVignetteCenterX) + (j - mVignetteCenterY)*(j - mVignetteCenterY);
+    return powf(F_E, -(powf((d_center_square/(mVignetteWidth*mVignetteWidth)),mVignetteGamma)/2.0f));
+}
+
 U32* LLImageRaw::getBrightnessHistogram()
 {
     if (!mHistoBrightness)
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index b62deadee02..b9f6a12bddc 100755
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -87,6 +87,13 @@ 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;
+
 //============================================================================
 // library initialization class
 
@@ -157,6 +164,13 @@ class LLImageBase : public LLThreadSafeRefCount
     U32 *mHistoBlue;
     U32 *mHistoBrightness;
     
+    // Vignette filtering
+    EVignetteMode mVignetteMode;
+    S32 mVignetteCenterX;
+    S32 mVignetteCenterY;
+    S32 mVignetteWidth;
+    F32 mVignetteGamma;
+    
 public:
 	static void generateMip(const U8 *indata, U8* mipdata, int width, int height, S32 nchannels);
 	
@@ -278,6 +292,7 @@ class LLImageRaw : public LLImageBase
     // Filter Primitives
     void colorTransform(const LLMatrix3 &transform);
     void colorCorrect(const U8* lut_red, const U8* lut_green, const U8* lut_blue);
+    void setVignette(EVignetteMode mode, F32 gamma);
     U32* getBrightnessHistogram();
 
 protected:
@@ -292,6 +307,7 @@ 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;
-- 
GitLab