diff options
author | Merov Linden <merov@lindenlab.com> | 2014-01-17 16:50:35 -0800 |
---|---|---|
committer | Merov Linden <merov@lindenlab.com> | 2014-01-17 16:50:35 -0800 |
commit | 95bb14440e4e35c7cc5c44a2b836deadb54bfd1b (patch) | |
tree | 81dbe1ba1f390eaa8dbc6aa8c95c4ae1f3724db8 /indra/llimage | |
parent | fda7b94f490564568dee0ba6d6516943b0fd82a0 (diff) |
ACME-1236 : Refactor vignette into stencil, implement uniform and gradient stencils, implement dodge and add blend modes
Diffstat (limited to 'indra/llimage')
-rwxr-xr-x | indra/llimage/llimagefilter.cpp | 308 | ||||
-rwxr-xr-x | indra/llimage/llimagefilter.h | 55 |
2 files changed, 226 insertions, 137 deletions
diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index cd03454cdd..03eabce8e3 100755 --- a/indra/llimage/llimagefilter.cpp +++ b/indra/llimage/llimagefilter.cpp @@ -46,9 +46,11 @@ LLImageFilter::LLImageFilter(const std::string& file_path) : mHistoGreen(NULL), mHistoBlue(NULL), mHistoBrightness(NULL), - mVignetteMode(VIGNETTE_MODE_NONE), - mVignetteGamma(1.0), - mVignetteMin(0.0) + 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); @@ -79,23 +81,13 @@ LLImageFilter::~LLImageFilter() * Add stencil (min,max) range * Suppress alpha from colorcorrect and use uniform alpha instead * Refactor stencil composition in the filter primitives + * Make filter definition resolution independent (do not use pixel size anywhere) - <array> - <string>stencil</string> - <string>shape</string> - <string>blend_mode</string> - <real>min</real> - <real>max</real> - <real>param1</real> - <real>param2</real> - <real>param3</real> - <real>param4</real> - </array> - - vignette : center_x, center_y, width, feather - sine : wavelength, angle - flat - gradient : start_x, start_y, end_x, end_y + params: + * vignette : center_x, center_y, width, feather + * scan lines : wavelength, angle + * uniform + * gradient : start_x, start_y, end_x, end_y * Document all the admissible names in the wiki @@ -143,17 +135,86 @@ void LLImageFilter::executeFilter(LLPointer<LLImageRaw> raw_image) //std::cout << std::endl; // Execute the filter described on this line - if (filter_name == "blend") + /* + <array> + <string>stencil</string> + <string>shape</string> uniform / gradient / vignette / scanlines + <string>blend_mode</string> blend /add /dodge / fade + <real>min</real> -1.0 to 1.0 (mandatory though ignored for uniform shape) + <real>max</real> -1.0 to 1.0 (value for uniform) + <real>param1</real> + <real>param2</real> + <real>param3</real> + <real>param4</real> + </array> + params: + * vignette : center_x, center_y, width, feather : positions between in float (0.0 is center, 1.0 is top), width in float in same unit, feather is a float + * scan lines : wavelength, angle : wavelength in float assuming (height/2 = 1), angle float in degree + * uniform : all parameters ignored + * gradient : start_x, start_y, end_x, end_y : position in float (0.0 is center, 1.0 is top) + */ + if (filter_name == "stencil") { - setVignette(VIGNETTE_MODE_BLEND,VIGNETTE_TYPE_CENTER,(float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal())); + // 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 alpha + 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 == "dodge") + { + mode = STENCIL_BLEND_MODE_DODGE; + } + 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 == "blend") + { + setStencil(STENCIL_BLEND_MODE_BLEND,STENCIL_SHAPE_VIGNETTE,(float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal()),1.0); } else if (filter_name == "fade") { - setVignette(VIGNETTE_MODE_FADE,VIGNETTE_TYPE_CENTER,(float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal())); + setStencil(STENCIL_BLEND_MODE_FADE,STENCIL_SHAPE_VIGNETTE,(float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal()),1.0); } else if (filter_name == "lines") { - setVignette(VIGNETTE_MODE_BLEND,VIGNETTE_TYPE_LINES,(float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal())); + setStencil(STENCIL_BLEND_MODE_BLEND,STENCIL_SHAPE_SCAN_LINES,(float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal()),1.0); } else if (filter_name == "sepia") { @@ -228,6 +289,38 @@ void LLImageFilter::executeFilter(LLPointer<LLImageRaw> raw_image) // 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] = pixel[VRED] + alpha * red; + pixel[VGREEN] = pixel[VGREEN] + alpha * green; + pixel[VBLUE] = pixel[VBLUE] + alpha * blue; + break; + case STENCIL_BLEND_MODE_DODGE: + // Dodge/burn the incoming color onto the background image + pixel[VRED] = inv_alpha * pixel[VRED] + red; + pixel[VGREEN] = inv_alpha * pixel[VGREEN] + green; + pixel[VBLUE] = 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(); @@ -241,31 +334,8 @@ void LLImageFilter::colorCorrect(const U8* lut_red, const U8* lut_green, const U { 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]]; - } - } + // 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; } } @@ -284,34 +354,13 @@ void LLImageFilter::colorTransform(const LLMatrix3 &transform) { 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); - 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]; - } - } + + // Blend result + blendStencil(getStencilAlpha(i,j), dst_data, dst.mV[VRED], dst.mV[VGREEN], dst.mV[VBLUE]); dst_data += components; } } @@ -333,6 +382,7 @@ void LLImageFilter::filterScreen(EScreenMode mode, const S32 wave_length, const { for (S32 i = 0; i < width; i++) { + // Compute screen value F32 value = 0.0; F32 d = 0.0; switch (mode) @@ -347,31 +397,8 @@ void LLImageFilter::filterScreen(EScreenMode mode, const S32 wave_length, const } 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; - } - } + // Blend result + blendStencil(getStencilAlpha(i,j), dst_data, dst_value, dst_value, dst_value); dst_data += components; } } @@ -381,36 +408,81 @@ void LLImageFilter::filterScreen(EScreenMode mode, const S32 wave_length, const // Procedural Stencils //============================================================================ -void LLImageFilter::setVignette(EVignetteMode mode, EVignetteType type, F32 gamma, F32 min) +void LLImageFilter::setStencil(EStencilBlendMode mode, EStencilShape type, F32 gamma, F32 min, F32 max) { - 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); + mStencilBlendMode = mode; + mStencilShape = type; + mStencilGamma = gamma; + mStencilMin = llmin(llmax(min, -1.0f), 1.0f); + mStencilMax = llmin(llmax(max, -1.0f), 1.0f); + + // We center the vignette on the image and fits it in the image smallest dimension + mStencilCenterX = mImage->getWidth()/2; + mStencilCenterY = mImage->getHeight()/2; + mStencilWidth = llmin(mImage->getWidth()/2,mImage->getHeight()/2); + + mStencilWavelength = gamma; + mStencilSine = 0.0; + mStencilCosine = 1.0; + + mStencilStartX = 0.0; + mStencilStartY = 0.0; + mStencilGradX = 0.0; + mStencilGradY = (F32)(mImage->getHeight()); + mStencilGradN = (F32)(mImage->getHeight()*mImage->getHeight()); } -F32 LLImageFilter::getVignetteAlpha(S32 i, S32 j) +void LLImageFilter::setStencil(EStencilShape shape, EStencilBlendMode mode, F32 min, F32 max, F32* params) { - F32 alpha = 1.0; - if (mVignetteType == VIGNETTE_TYPE_CENTER) + 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 - mVignetteCenterX)*(i - mVignetteCenterX) + (j - mVignetteCenterY)*(j - mVignetteCenterY); - alpha = powf(F_E, -(powf((d_center_square/(mVignetteWidth*mVignetteWidth)),mVignetteGamma)/2.0f)); + 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 (mVignetteType == VIGNETTE_TYPE_LINES) + else if (mStencilShape == STENCIL_SHAPE_SCAN_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); + // 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); } - // We rescale alpha between min and 1.0 so to avoid complete fading if so desired. - return (mVignetteMin + alpha * (1.0 - mVignetteMin)); + 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)); } //============================================================================ diff --git a/indra/llimage/llimagefilter.h b/indra/llimage/llimagefilter.h index dc092a47a5..19ac7e81d6 100755 --- a/indra/llimage/llimagefilter.h +++ b/indra/llimage/llimagefilter.h @@ -34,18 +34,21 @@ class LLColor4U; class LLColor3; class LLMatrix3; -typedef enum e_vignette_mode +typedef enum e_stencil_blend_mode { - VIGNETTE_MODE_NONE = 0, - VIGNETTE_MODE_BLEND = 1, - VIGNETTE_MODE_FADE = 2 -} EVignetteMode; + STENCIL_BLEND_MODE_BLEND = 0, + STENCIL_BLEND_MODE_ADD = 1, + STENCIL_BLEND_MODE_DODGE = 2, + STENCIL_BLEND_MODE_FADE = 3 +} EStencilBlendMode; -typedef enum e_vignette_type +typedef enum e_stencil_shape { - VIGNETTE_TYPE_CENTER = 0, - VIGNETTE_TYPE_LINES = 1 -} EVignetteType; + STENCIL_SHAPE_UNIFORM = 0, + STENCIL_SHAPE_GRADIENT = 1, + STENCIL_SHAPE_VIGNETTE = 2, + STENCIL_SHAPE_SCAN_LINES = 3 +} EStencilShape; typedef enum e_screen_mode { @@ -87,10 +90,12 @@ private: 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 blendStencil(F32 alpha, U8* pixel, U8 red, U8 green, U8 blue); // Procedural Stencils - void setVignette(EVignetteMode mode, EVignetteType type, F32 gamma, F32 min); - F32 getVignetteAlpha(S32 i, S32 j); + void setStencil(EStencilBlendMode mode, EStencilShape type, F32 gamma, F32 min, F32 max); + void setStencil(EStencilShape shape, EStencilBlendMode mode, F32 min, F32 max, F32* params); + F32 getStencilAlpha(S32 i, S32 j); // Histograms U32* getBrightnessHistogram(); @@ -105,14 +110,26 @@ private: U32 *mHistoBlue; U32 *mHistoBrightness; - // Vignette filtering - EVignetteMode mVignetteMode; - EVignetteType mVignetteType; - S32 mVignetteCenterX; - S32 mVignetteCenterY; - S32 mVignetteWidth; - F32 mVignetteGamma; - F32 mVignetteMin; + // 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; }; |