From 54ad7de61b5be2014c2e061a4964b7d6620a5dd4 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Mon, 13 Jan 2014 17:01:44 -0800 Subject: ACME-1244 : Introduced llimage/llimagefilter, refactored the filter execution code, simplified llimage_libtest filter testing --- indra/llimage/llimagefilter.cpp | 269 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100755 indra/llimage/llimagefilter.cpp (limited to 'indra/llimage/llimagefilter.cpp') diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp new file mode 100755 index 0000000000..e2d281e48d --- /dev/null +++ b/indra/llimage/llimagefilter.cpp @@ -0,0 +1,269 @@ +/** + * @file llimagefilter.cpp + * @brief Simple Image Filtering. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2014, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llimagefilter.h" + +#include "llmath.h" +#include "v3color.h" +#include "v4coloru.h" +#include "m3math.h" +#include "v3math.h" +#include "llsdserialize.h" + +//--------------------------------------------------------------------------- +// LLImageFilter +//--------------------------------------------------------------------------- + +LLImageFilter::LLImageFilter() : + mFilterData(LLSD::emptyArray()) +{ +} + +LLImageFilter::~LLImageFilter() +{ +} + +/* + " -f, --filter []\n" + " Apply the filter to the input images using the optional value. Admissible names:\n" + " - 'grayscale' converts to grayscale (no param).\n" + " - 'sepia' converts to sepia (no param).\n" + " - 'saturate' changes color saturation according to : < 1.0 will desaturate, > 1.0 will saturate.\n" + " - 'rotate' rotates the color hue according to (in degree, positive value only).\n" + " - 'gamma' applies gamma curve to all channels: > 1.0 will darken, < 1.0 will lighten.\n" + " - 'colorize' applies a red tint to the image using as an alpha (transparency between 0.0 and 1.0) value.\n" + " - 'contrast' modifies the contrast according to : > 1.0 will enhance the contrast, <1.0 will flatten it.\n" + " - 'brighten' adds light to the image ( between 0 and 255).\n" + " - 'darken' substracts light to the image ( between 0 and 255).\n" + " - 'linearize' optimizes the contrast using the brightness histogram. is the fraction (between 0.0 and 1.0) of discarded tail of the histogram.\n" + " - 'posterize' redistributes the colors between classes per channel ( between 2 and 255).\n" + " - 'newsscreen' applies a 2D sine screening to the red channel and output to black and white.\n" + " - 'horizontalscreen' applies a horizontal screening to the red channel and output to black and white.\n" + " - '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 [ ]\n" + " Apply a circular central vignette to the filter using the optional and 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 +void LLImageFilter::loadFromFile(const std::string& file_path) +{ + //std::cout << "Loading filter settings from : " << file_path << std::endl; + llifstream filter_xml(file_path); + if (filter_xml.is_open()) + { + // Load and parse the file + LLPointer parser = new LLSDXMLParser(); + parser->parse(filter_xml, mFilterData, LLSDSerialize::SIZE_UNLIMITED); + filter_xml.close(); + } + else + { + // File couldn't be open, reset the filter data + mFilterData = LLSD(); + } +} + +// Apply the filter data to the image passed as parameter +void LLImageFilter::executeFilter(LLPointer raw_image) +{ + //std::cout << "Filter : size = " << mFilterData.size() << std::endl; + for (S32 i = 0; i < mFilterData.size(); ++i) + { + std::string filter_name = mFilterData[i][0].asString(); + // Dump out the filter values (for debug) + //std::cout << "Filter : name = " << mFilterData[i][0].asString() << ", params = "; + //for (S32 j = 1; j < mFilterData[i].size(); ++j) + //{ + // std::cout << mFilterData[i][j].asString() << ", "; + //} + //std::cout << std::endl; + + // 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())); + } + else if (filter_name == "fade") + { + raw_image->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())); + } + else if (filter_name == "sepia") + { + raw_image->filterSepia(); + } + else if (filter_name == "grayscale") + { + raw_image->filterGrayScale(); + } + else if (filter_name == "saturate") + { + raw_image->filterSaturate((float)(mFilterData[i][1].asReal())); + } + else if (filter_name == "rotate") + { + raw_image->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); + } + 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); + } + 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); + } + 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); + } + 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); + } + 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); + } + 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); + } + else if (filter_name == "screen") + { + std::string screen_name = mFilterData[i][1].asString(); + EScreenMode mode = SCREEN_MODE_2DSINE; + if (screen_name == "2Dsine") + { + mode = SCREEN_MODE_2DSINE; + } + else if (screen_name == "line") + { + mode = SCREEN_MODE_LINE; + } + raw_image->filterScreen(mode,(S32)(mFilterData[i][2].asReal()),(F32)(mFilterData[i][3].asReal())); + } + } +} + + +//============================================================================ -- cgit v1.2.3 From 7cc64a09a3ca9211354427206f04d157c9ac30a2 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Tue, 14 Jan 2014 21:01:51 -0800 Subject: ACME-1236 : Refactor filters and vignette into llimagefilter, add example filters to llimage_libtest --- indra/llimage/llimagefilter.cpp | 641 ++++++++++++++++++++++++++++++++++------ 1 file changed, 547 insertions(+), 94 deletions(-) (limited to 'indra/llimage/llimagefilter.cpp') diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index e2d281e48d..ed97d9ae17 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 []\n" " Apply the filter to the input images using the optional 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 [ ]\n" + " Apply a circular central vignette to the filter using the optional and 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 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 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 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); +} //============================================================================ -- cgit v1.2.3 From f1229912280fe67b272f5c1ba424524d738ed867 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Wed, 15 Jan 2014 09:24:06 -0800 Subject: ACME-1236 : Add comments to LLImageFilter --- indra/llimage/llimagefilter.cpp | 46 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'indra/llimage/llimagefilter.cpp') diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index ed97d9ae17..5c969001b7 100755 --- a/indra/llimage/llimagefilter.cpp +++ b/indra/llimage/llimagefilter.cpp @@ -62,6 +62,34 @@ LLImageFilter::~LLImageFilter() } /* + *TODO + * Rename vignette to stencil + * Separate shape from mode + * Add shapes : uniform and gradients + * Add modes + * Add stencil (min,max) range + * Suppress alpha from colorcorrect and use uniform alpha instead + * Refactor stencil composition in the filter primitives + + + stencil + shape + blend_mode + min + max + param1 + param2 + param3 + param4 + + + vignette : center_x, center_y, width, feather + sine : wavelength, angle + flat + gradient : start_x, start_y, end_x, end_y + + * Document all the admissible names in the wiki + " Apply the filter to the input images using the optional value. Admissible names:\n" " - 'grayscale' converts to grayscale (no param).\n" " - 'sepia' converts to sepia (no param).\n" @@ -85,7 +113,10 @@ LLImageFilter::~LLImageFilter() " - 'fade' : the filter is applied with full intensity in the center and fades to black to the periphery.\n" */ +//============================================================================ // Load filter from file +//============================================================================ + void LLImageFilter::loadFromFile(const std::string& file_path) { //std::cout << "Loading filter settings from : " << file_path << std::endl; @@ -104,7 +135,10 @@ void LLImageFilter::loadFromFile(const std::string& file_path) } } +//============================================================================ // Apply the filter data to the image passed as parameter +//============================================================================ + void LLImageFilter::executeFilter(LLPointer raw_image) { mImage = raw_image; @@ -203,7 +237,10 @@ void LLImageFilter::executeFilter(LLPointer raw_image) } } +//============================================================================ // Filter Primitives +//============================================================================ + void LLImageFilter::colorCorrect(const U8* lut_red, const U8* lut_green, const U8* lut_blue) { const S32 components = mImage->getComponents(); @@ -353,7 +390,10 @@ void LLImageFilter::filterScreen(EScreenMode mode, const S32 wave_length, const } } +//============================================================================ // Procedural Stencils +//============================================================================ + void LLImageFilter::setVignette(EVignetteMode mode, EVignetteType type, F32 gamma, F32 min) { mVignetteMode = mode; @@ -386,7 +426,10 @@ F32 LLImageFilter::getVignetteAlpha(S32 i, S32 j) return (mVignetteMin + alpha * (1.0 - mVignetteMin)); } +//============================================================================ // Histograms +//============================================================================ + U32* LLImageFilter::getBrightnessHistogram() { if (!mHistoBrightness) @@ -444,7 +487,10 @@ void LLImageFilter::computeHistograms() } } +//============================================================================ // Secondary Filters +//============================================================================ + void LLImageFilter::filterGrayScale() { LLMatrix3 gray_scale; -- cgit v1.2.3 From fda7b94f490564568dee0ba6d6516943b0fd82a0 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Thu, 16 Jan 2014 18:34:51 -0800 Subject: ACME-1236 : Small LLImageFilter clean up --- indra/llimage/llimagefilter.cpp | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-) (limited to 'indra/llimage/llimagefilter.cpp') diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index 5c969001b7..cd03454cdd 100755 --- a/indra/llimage/llimagefilter.cpp +++ b/indra/llimage/llimagefilter.cpp @@ -39,7 +39,7 @@ // LLImageFilter //--------------------------------------------------------------------------- -LLImageFilter::LLImageFilter() : +LLImageFilter::LLImageFilter(const std::string& file_path) : mFilterData(LLSD::emptyArray()), mImage(NULL), mHistoRed(NULL), @@ -50,6 +50,15 @@ LLImageFilter::LLImageFilter() : mVignetteGamma(1.0), mVignetteMin(0.0) { + // Load filter description from file + llifstream filter_xml(file_path); + if (filter_xml.is_open()) + { + // Load and parse the file + LLPointer parser = new LLSDXMLParser(); + parser->parse(filter_xml, mFilterData, LLSDSerialize::SIZE_UNLIMITED); + filter_xml.close(); + } } LLImageFilter::~LLImageFilter() @@ -113,28 +122,6 @@ LLImageFilter::~LLImageFilter() " - 'fade' : the filter is applied with full intensity in the center and fades to black to the periphery.\n" */ -//============================================================================ -// Load filter from file -//============================================================================ - -void LLImageFilter::loadFromFile(const std::string& file_path) -{ - //std::cout << "Loading filter settings from : " << file_path << std::endl; - llifstream filter_xml(file_path); - if (filter_xml.is_open()) - { - // Load and parse the file - LLPointer parser = new LLSDXMLParser(); - parser->parse(filter_xml, mFilterData, LLSDSerialize::SIZE_UNLIMITED); - filter_xml.close(); - } - else - { - // File couldn't be open, reset the filter data - mFilterData = LLSD(); - } -} - //============================================================================ // Apply the filter data to the image passed as parameter //============================================================================ -- cgit v1.2.3 From 95bb14440e4e35c7cc5c44a2b836deadb54bfd1b Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Fri, 17 Jan 2014 16:50:35 -0800 Subject: ACME-1236 : Refactor vignette into stencil, implement uniform and gradient stencils, implement dodge and add blend modes --- indra/llimage/llimagefilter.cpp | 308 +++++++++++++++++++++++++--------------- 1 file changed, 190 insertions(+), 118 deletions(-) (limited to 'indra/llimage/llimagefilter.cpp') 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) - - stencil - shape - blend_mode - min - max - param1 - param2 - param3 - param4 - - - 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 raw_image) //std::cout << std::endl; // Execute the filter described on this line - if (filter_name == "blend") + /* + + stencil + shape uniform / gradient / vignette / scanlines + blend_mode blend /add /dodge / fade + min -1.0 to 1.0 (mandatory though ignored for uniform shape) + max -1.0 to 1.0 (value for uniform) + param1 + param2 + param3 + param4 + + 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 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)); } //============================================================================ -- cgit v1.2.3 From d14392f55f9c47ba121d5470a3deb153a16b1cfb Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Mon, 20 Jan 2014 19:52:27 -0800 Subject: ACME-1236 : Fix new blend modes, updated comments --- indra/llimage/llimagefilter.cpp | 43 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 22 deletions(-) (limited to 'indra/llimage/llimagefilter.cpp') diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index 03eabce8e3..0f11b1037f 100755 --- a/indra/llimage/llimagefilter.cpp +++ b/indra/llimage/llimagefilter.cpp @@ -74,14 +74,12 @@ LLImageFilter::~LLImageFilter() /* *TODO - * Rename vignette to stencil - * Separate shape from mode - * Add shapes : uniform and gradients - * Add modes - * Add stencil (min,max) range - * Suppress alpha from colorcorrect and use uniform alpha instead - * Refactor stencil composition in the filter primitives + * Test blend modes + * Improve perf: use LUT for alpha blending in uniform case + * Improve perf: make sure filter is not called more than necessary in viewer (seems to be called 3 times per change) * Make filter definition resolution independent (do not use pixel size anywhere) + * Add gradient coloring as a filter + * Add convolve3x3 params: * vignette : center_x, center_y, width, feather @@ -96,18 +94,19 @@ LLImageFilter::~LLImageFilter() " - 'sepia' converts to sepia (no param).\n" " - 'saturate' changes color saturation according to : < 1.0 will desaturate, > 1.0 will saturate.\n" " - 'rotate' rotates the color hue according to (in degree, positive value only).\n" - " - 'gamma' applies gamma curve to all channels: > 1.0 will darken, < 1.0 will lighten.\n" - " - 'colorize' applies a red tint to the image using as an alpha (transparency between 0.0 and 1.0) value.\n" + + " - 'gamma' applies a gamma curve to all channels: > 1.0 will darken, < 1.0 will lighten.\n" + " - 'colorize' applies a colored tint to the image.\n" " - 'contrast' modifies the contrast according to : > 1.0 will enhance the contrast, <1.0 will flatten it.\n" " - 'brighten' adds light to the image ( between 0 and 255).\n" " - 'darken' substracts light to the image ( between 0 and 255).\n" - " - 'linearize' optimizes the contrast using the brightness histogram. is the fraction (between 0.0 and 1.0) of discarded tail of the histogram.\n" + " - 'linearize' optimizes the contrast using the brightness histogram. is the fraction (between 0.0 and 1.0) of the discarded head and tail of the histogram.\n" " - 'posterize' redistributes the colors between classes per channel ( between 2 and 255).\n" - " - 'newsscreen' applies a 2D sine screening to the red channel and output to black and white.\n" - " - 'horizontalscreen' applies a horizontal screening to the red channel and output to black and white.\n" - " - '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" + + " - 'screen' applies a screening filter to the red channel and output to black and white. This filter assumes that the input image has been converted to grayscale or that the red channel is somewhat meaningful. It takes 3 parameters: a mode, a wave length and an angle. Modes are:\n" + " - '2Dsine' applies a bidirectional (x,y) sine screen. has no influence on that mode.\n" + " - 'line' applies a linear sine screen. is the line generator angle with the horizontal.\n" + " is size between 2 peaks of the sine function in normalized image coordinates." " Apply a circular central vignette to the filter using the optional and values. Admissible names:\n" " - 'blend' : the filter is applied with full intensity in the center and blends with the image to the periphery.\n" @@ -174,7 +173,7 @@ void LLImageFilter::executeFilter(LLPointer raw_image) { 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 + // Get the blend mode of the stencil, that is how the effect is blended in the background through the stencil std::string filter_mode = mFilterData[i][2].asString(); EStencilBlendMode mode = STENCIL_BLEND_MODE_BLEND; if (filter_mode == "blend") @@ -302,15 +301,15 @@ void LLImageFilter::blendStencil(F32 alpha, U8* pixel, U8 red, U8 green, U8 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; + pixel[VRED] = llclampb(pixel[VRED] + alpha * red); + pixel[VGREEN] = llclampb(pixel[VGREEN] + alpha * green); + pixel[VBLUE] = llclampb(pixel[VBLUE] + alpha * blue); break; case STENCIL_BLEND_MODE_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; + pixel[VRED] = llclampb(inv_alpha * pixel[VRED] + red); + pixel[VGREEN] = llclampb(inv_alpha * pixel[VGREEN] + green); + pixel[VBLUE] = llclampb(inv_alpha * pixel[VBLUE] + blue); break; case STENCIL_BLEND_MODE_FADE: // Fade incoming color to black -- cgit v1.2.3 From 6c630b73a825befb6eeef66d7ed0063b1b891df7 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Tue, 21 Jan 2014 13:05:54 -0800 Subject: ACME-1240 : Implement convolve filter for 3x3 kernels. Implements sharpen, blur and edge detection as examples and tests. --- indra/llimage/llimagefilter.cpp | 164 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) (limited to 'indra/llimage/llimagefilter.cpp') diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index 0f11b1037f..75661a6d4b 100755 --- a/indra/llimage/llimagefilter.cpp +++ b/indra/llimage/llimagefilter.cpp @@ -34,6 +34,7 @@ #include "m3math.h" #include "v3math.h" #include "llsdserialize.h" +#include "llstring.h" //--------------------------------------------------------------------------- // LLImageFilter @@ -281,6 +282,32 @@ void LLImageFilter::executeFilter(LLPointer raw_image) } filterScreen(mode,(S32)(mFilterData[i][2].asReal()),(F32)(mFilterData[i][3].asReal())); } + else if (filter_name == "blur") + { + LLMatrix3 kernel; + for (S32 i = 0; i < NUM_VALUES_IN_MAT3; i++) + for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++) + kernel.mMatrix[i][j] = 1.0; + convolve(kernel,true,false); + } + else if (filter_name == "sharpen") + { + LLMatrix3 kernel; + for (S32 i = 0; i < NUM_VALUES_IN_MAT3; i++) + for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++) + kernel.mMatrix[i][j] = -1.0; + kernel.mMatrix[1][1] = 9.0; + convolve(kernel,false,false); + } + else if (filter_name == "gradient") + { + LLMatrix3 kernel; + for (S32 i = 0; i < NUM_VALUES_IN_MAT3; i++) + for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++) + kernel.mMatrix[i][j] = -1.0; + kernel.mMatrix[1][1] = 8.0; + convolve(kernel,false,true); + } } } @@ -365,6 +392,143 @@ void LLImageFilter::colorTransform(const LLMatrix3 &transform) } } +void LLImageFilter::convolve(const LLMatrix3 &kernel, bool normalize, bool abs_value) +{ + const S32 components = mImage->getComponents(); + llassert( components >= 1 && components <= 4 ); + + // Compute normalization factors + F32 kernel_min = 0.0; + F32 kernel_max = 0.0; + for (S32 i = 0; i < NUM_VALUES_IN_MAT3; i++) + { + for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++) + { + if (kernel.mMatrix[i][j] >= 0.0) + kernel_max += kernel.mMatrix[i][j]; + else + kernel_min += kernel.mMatrix[i][j]; + } + } + if (abs_value) + { + kernel_max = llabs(kernel_max); + kernel_min = llabs(kernel_min); + kernel_max = llmax(kernel_max,kernel_min); + kernel_min = 0.0; + } + F32 kernel_range = kernel_max - kernel_min; + + // Allocate temporary buffers and initialize algorithm's data + S32 width = mImage->getWidth(); + S32 height = mImage->getHeight(); + + U8* dst_data = mImage->getData(); + + S32 buffer_size = width * components; + llassert_always(buffer_size > 0); + std::vector even_buffer(buffer_size); + std::vector odd_buffer(buffer_size); + + U8* south_data = dst_data + buffer_size; + U8* east_west_data; + U8* north_data; + + // Line 0 : we set the line to 0 (debatable) + memcpy( &even_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */ + for (S32 i = 0; i < width; i++) + { + blendStencil(getStencilAlpha(i,0), dst_data, 0, 0, 0); + dst_data += components; + } + south_data += buffer_size; + + // All other lines + for (S32 j = 1; j < (height-1); j++) + { + // We need to buffer 2 lines. We flip north and current to avoid moving too much memory around + if (j % 2) + { + memcpy( &odd_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */ + east_west_data = &odd_buffer[0]; + north_data = &even_buffer[0]; + } + else + { + memcpy( &even_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */ + east_west_data = &even_buffer[0]; + north_data = &odd_buffer[0]; + } + // First pixel : set to 0 + blendStencil(getStencilAlpha(0,j), dst_data, 0, 0, 0); + // Set pointers to kernel + U8* NW = north_data; + U8* N = NW+components; + U8* NE = N+components; + U8* W = east_west_data; + U8* C = W+components; + U8* E = C+components; + U8* SW = south_data; + U8* S = SW+components; + U8* SE = S+components; + dst_data += components; + // All other pixels + for (S32 i = 1; i < (width-1); i++) + { + // Compute convolution + LLVector3 dst; + dst.mV[VRED] = (kernel.mMatrix[0][0]*NW[VRED] + kernel.mMatrix[0][1]*N[VRED] + kernel.mMatrix[0][2]*NE[VRED] + + kernel.mMatrix[1][0]*W[VRED] + kernel.mMatrix[1][1]*C[VRED] + kernel.mMatrix[1][2]*E[VRED] + + kernel.mMatrix[2][0]*SW[VRED] + kernel.mMatrix[2][1]*S[VRED] + kernel.mMatrix[2][2]*SE[VRED]); + dst.mV[VGREEN] = (kernel.mMatrix[0][0]*NW[VGREEN] + kernel.mMatrix[0][1]*N[VGREEN] + kernel.mMatrix[0][2]*NE[VGREEN] + + kernel.mMatrix[1][0]*W[VGREEN] + kernel.mMatrix[1][1]*C[VGREEN] + kernel.mMatrix[1][2]*E[VGREEN] + + kernel.mMatrix[2][0]*SW[VGREEN] + kernel.mMatrix[2][1]*S[VGREEN] + kernel.mMatrix[2][2]*SE[VGREEN]); + dst.mV[VBLUE] = (kernel.mMatrix[0][0]*NW[VBLUE] + kernel.mMatrix[0][1]*N[VBLUE] + kernel.mMatrix[0][2]*NE[VBLUE] + + kernel.mMatrix[1][0]*W[VBLUE] + kernel.mMatrix[1][1]*C[VBLUE] + kernel.mMatrix[1][2]*E[VBLUE] + + kernel.mMatrix[2][0]*SW[VBLUE] + kernel.mMatrix[2][1]*S[VBLUE] + kernel.mMatrix[2][2]*SE[VBLUE]); + if (abs_value) + { + dst.mV[VRED] = llabs(dst.mV[VRED]); + dst.mV[VGREEN] = llabs(dst.mV[VGREEN]); + dst.mV[VBLUE] = llabs(dst.mV[VBLUE]); + } + if (normalize) + { + dst.mV[VRED] = (dst.mV[VRED] - kernel_min)/kernel_range; + dst.mV[VGREEN] = (dst.mV[VGREEN] - kernel_min)/kernel_range; + dst.mV[VBLUE] = (dst.mV[VBLUE] - kernel_min)/kernel_range; + } + dst.clamp(0.0f,255.0f); + + // Blend result + blendStencil(getStencilAlpha(i,j), dst_data, dst.mV[VRED], dst.mV[VGREEN], dst.mV[VBLUE]); + + // Next pixel + dst_data += components; + NW += components; + N += components; + NE += components; + W += components; + C += components; + E += components; + SW += components; + S += components; + SE += components; + } + // Last pixel : set to 0 + blendStencil(getStencilAlpha(width-1,j), dst_data, 0, 0, 0); + dst_data += components; + south_data += buffer_size; + } + + // Last line + for (S32 i = 0; i < width; i++) + { + blendStencil(getStencilAlpha(i,0), dst_data, 0, 0, 0); + dst_data += components; + } +} + void LLImageFilter::filterScreen(EScreenMode mode, const S32 wave_length, const F32 angle) { const S32 components = mImage->getComponents(); -- cgit v1.2.3 From 4082d8ea27a6d386a01681e51e9776b552f6cb5c Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Tue, 21 Jan 2014 16:39:23 -0800 Subject: ACME-1236 : Fixes gamma and brightness code. Also fixes example xml filter files. --- indra/llimage/llimagefilter.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'indra/llimage/llimagefilter.cpp') diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index 75661a6d4b..c02679e04f 100755 --- a/indra/llimage/llimagefilter.cpp +++ b/indra/llimage/llimagefilter.cpp @@ -80,7 +80,6 @@ LLImageFilter::~LLImageFilter() * Improve perf: make sure filter is not called more than necessary in viewer (seems to be called 3 times per change) * Make filter definition resolution independent (do not use pixel size anywhere) * Add gradient coloring as a filter - * Add convolve3x3 params: * vignette : center_x, center_y, width, feather @@ -251,12 +250,12 @@ void LLImageFilter::executeFilter(LLPointer raw_image) else if (filter_name == "brighten") { LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal())); - filterBrightness((S32)(mFilterData[i][1].asReal()),color); + filterBrightness((float)(mFilterData[i][1].asReal()),color); } else if (filter_name == "darken") { LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal())); - filterBrightness((S32)(-mFilterData[i][1].asReal()),color); + filterBrightness((float)(-mFilterData[i][1].asReal()),color); } else if (filter_name == "linearize") { @@ -807,7 +806,7 @@ void LLImageFilter::filterGamma(F32 gamma, const LLColor3& alpha) for (S32 i = 0; i < 256; i++) { - F32 gamma_i = llclampf((float)(powf((float)(i)/255.0,gamma))); + F32 gamma_i = llclampf((float)(powf((float)(i)/255.0,1.0/gamma))); // Blend in with alpha values gamma_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * 255.0 * gamma_i); gamma_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * 255.0 * gamma_i); @@ -969,15 +968,17 @@ void LLImageFilter::filterContrast(F32 slope, const LLColor3& alpha) colorCorrect(contrast_red_lut,contrast_green_lut,contrast_blue_lut); } -void LLImageFilter::filterBrightness(S32 add, const LLColor3& alpha) +void LLImageFilter::filterBrightness(F32 add, const LLColor3& alpha) { U8 brightness_red_lut[256]; U8 brightness_green_lut[256]; U8 brightness_blue_lut[256]; + S32 add_value = (S32)(add * 255.0); + for (S32 i = 0; i < 256; i++) { - U8 value_i = (U8)(llclampb((S32)((S32)(i) + add))); + U8 value_i = (U8)(llclampb(i + add_value)); // Blend in with alpha values brightness_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i); brightness_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i); -- cgit v1.2.3 From c99e22e6740ebb3e39bc1b85000bd465676e60d1 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Tue, 21 Jan 2014 21:17:57 -0800 Subject: ACME-1236 : Make all filters resolution independent, cleanup comments, move example filters to a sub folder in llimage_libtest --- indra/llimage/llimagefilter.cpp | 70 +++++++---------------------------------- 1 file changed, 11 insertions(+), 59 deletions(-) (limited to 'indra/llimage/llimagefilter.cpp') diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index c02679e04f..4c6dd2faee 100755 --- a/indra/llimage/llimagefilter.cpp +++ b/indra/llimage/llimagefilter.cpp @@ -1,6 +1,6 @@ /** * @file llimagefilter.cpp - * @brief Simple Image Filtering. + * @brief Simple Image Filtering. See https://wiki.lindenlab.com/wiki/SL_Viewer_Image_Filters for complete documentation. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code @@ -75,42 +75,12 @@ LLImageFilter::~LLImageFilter() /* *TODO - * Test blend modes + * Rename stencil to mask + * Test blend modes and name them correctly + * Suppress old "blend", "fade" and "lines" stencil definition. Change all xml accordingly. * Improve perf: use LUT for alpha blending in uniform case * Improve perf: make sure filter is not called more than necessary in viewer (seems to be called 3 times per change) - * Make filter definition resolution independent (do not use pixel size anywhere) * Add gradient coloring as a filter - - 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 - - " Apply the filter to the input images using the optional value. Admissible names:\n" - " - 'grayscale' converts to grayscale (no param).\n" - " - 'sepia' converts to sepia (no param).\n" - " - 'saturate' changes color saturation according to : < 1.0 will desaturate, > 1.0 will saturate.\n" - " - 'rotate' rotates the color hue according to (in degree, positive value only).\n" - - " - 'gamma' applies a gamma curve to all channels: > 1.0 will darken, < 1.0 will lighten.\n" - " - 'colorize' applies a colored tint to the image.\n" - " - 'contrast' modifies the contrast according to : > 1.0 will enhance the contrast, <1.0 will flatten it.\n" - " - 'brighten' adds light to the image ( between 0 and 255).\n" - " - 'darken' substracts light to the image ( between 0 and 255).\n" - " - 'linearize' optimizes the contrast using the brightness histogram. is the fraction (between 0.0 and 1.0) of the discarded head and tail of the histogram.\n" - " - 'posterize' redistributes the colors between classes per channel ( between 2 and 255).\n" - - " - 'screen' applies a screening filter to the red channel and output to black and white. This filter assumes that the input image has been converted to grayscale or that the red channel is somewhat meaningful. It takes 3 parameters: a mode, a wave length and an angle. Modes are:\n" - " - '2Dsine' applies a bidirectional (x,y) sine screen. has no influence on that mode.\n" - " - 'line' applies a linear sine screen. is the line generator angle with the horizontal.\n" - " is size between 2 peaks of the sine function in normalized image coordinates." - - " Apply a circular central vignette to the filter using the optional and 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" */ //============================================================================ @@ -133,25 +103,6 @@ void LLImageFilter::executeFilter(LLPointer raw_image) //} //std::cout << std::endl; - // Execute the filter described on this line - /* - - stencil - shape uniform / gradient / vignette / scanlines - blend_mode blend /add /dodge / fade - min -1.0 to 1.0 (mandatory though ignored for uniform shape) - max -1.0 to 1.0 (value for uniform) - param1 - param2 - param3 - param4 - - 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") { // Get the shape of the stencil, that is how the procedural alpha is computed geometrically @@ -279,7 +230,7 @@ void LLImageFilter::executeFilter(LLPointer raw_image) { mode = SCREEN_MODE_LINE; } - filterScreen(mode,(S32)(mFilterData[i][2].asReal()),(F32)(mFilterData[i][3].asReal())); + filterScreen(mode,(F32)(mFilterData[i][2].asReal()),(F32)(mFilterData[i][3].asReal())); } else if (filter_name == "blur") { @@ -445,7 +396,7 @@ void LLImageFilter::convolve(const LLMatrix3 &kernel, bool normalize, bool abs_v // All other lines for (S32 j = 1; j < (height-1); j++) { - // We need to buffer 2 lines. We flip north and current to avoid moving too much memory around + // We need to buffer 2 lines. We flip north and east-west (current) to avoid moving too much memory around if (j % 2) { memcpy( &odd_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */ @@ -460,6 +411,7 @@ void LLImageFilter::convolve(const LLMatrix3 &kernel, bool normalize, bool abs_v } // First pixel : set to 0 blendStencil(getStencilAlpha(0,j), dst_data, 0, 0, 0); + dst_data += components; // Set pointers to kernel U8* NW = north_data; U8* N = NW+components; @@ -470,7 +422,6 @@ void LLImageFilter::convolve(const LLMatrix3 &kernel, bool normalize, bool abs_v U8* SW = south_data; U8* S = SW+components; U8* SE = S+components; - dst_data += components; // All other pixels for (S32 i = 1; i < (width-1); i++) { @@ -528,7 +479,7 @@ void LLImageFilter::convolve(const LLMatrix3 &kernel, bool normalize, bool abs_v } } -void LLImageFilter::filterScreen(EScreenMode mode, const S32 wave_length, const F32 angle) +void LLImageFilter::filterScreen(EScreenMode mode, const F32 wave_length, const F32 angle) { const S32 components = mImage->getComponents(); llassert( components >= 1 && components <= 4 ); @@ -536,6 +487,7 @@ void LLImageFilter::filterScreen(EScreenMode mode, const S32 wave_length, const S32 width = mImage->getWidth(); S32 height = mImage->getHeight(); + F32 wave_length_pixels = wave_length * (F32)(height) / 2.0; F32 sin = sinf(angle*DEG_TO_RAD); F32 cos = cosf(angle*DEG_TO_RAD); @@ -550,11 +502,11 @@ void LLImageFilter::filterScreen(EScreenMode mode, const S32 wave_length, const 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; + value = (sinf(2*F_PI*i/wave_length_pixels)*sinf(2*F_PI*j/wave_length_pixels)+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; + value = (sinf(2*F_PI*d/wave_length_pixels)+1.0)*255.0/2.0; break; } U8 dst_value = (dst_data[VRED] >= (U8)(value) ? 255 : 0); -- cgit v1.2.3 From ff3edd06d2c824a32753e689de3369abf593c684 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Thu, 23 Jan 2014 16:37:48 -0800 Subject: ACME-1275 : WIP : Clean up unwanted code and comments in image filter --- indra/llimage/llimagefilter.cpp | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) (limited to 'indra/llimage/llimagefilter.cpp') diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index 4c6dd2faee..e0dae9fab2 100755 --- a/indra/llimage/llimagefilter.cpp +++ b/indra/llimage/llimagefilter.cpp @@ -77,7 +77,6 @@ LLImageFilter::~LLImageFilter() *TODO * Rename stencil to mask * Test blend modes and name them correctly - * Suppress old "blend", "fade" and "lines" stencil definition. Change all xml accordingly. * Improve perf: use LUT for alpha blending in uniform case * Improve perf: make sure filter is not called more than necessary in viewer (seems to be called 3 times per change) * Add gradient coloring as a filter @@ -154,18 +153,6 @@ void LLImageFilter::executeFilter(LLPointer raw_image) // 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") - { - 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") - { - 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") { filterSepia(); @@ -258,6 +245,10 @@ void LLImageFilter::executeFilter(LLPointer raw_image) kernel.mMatrix[1][1] = 8.0; convolve(kernel,false,true); } + else + { + llwarns << "Filter unknown, cannot execute filter command : " << filter_name << llendl; + } } } -- cgit v1.2.3 From 225fb4e782108d83217b587188beb031afb04fef Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Wed, 5 Feb 2014 18:56:58 -0800 Subject: ACME-1300 : Rename blend mode dodge to add_back --- indra/llimage/llimagefilter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indra/llimage/llimagefilter.cpp') diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index e0dae9fab2..7734b08c18 100755 --- a/indra/llimage/llimagefilter.cpp +++ b/indra/llimage/llimagefilter.cpp @@ -134,9 +134,9 @@ void LLImageFilter::executeFilter(LLPointer raw_image) { mode = STENCIL_BLEND_MODE_ADD; } - else if (filter_mode == "dodge") + else if (filter_mode == "add_back") { - mode = STENCIL_BLEND_MODE_DODGE; + mode = STENCIL_BLEND_MODE_ABACK; } else if (filter_mode == "fade") { @@ -273,8 +273,8 @@ void LLImageFilter::blendStencil(F32 alpha, U8* pixel, U8 red, U8 green, U8 blue pixel[VGREEN] = llclampb(pixel[VGREEN] + alpha * green); pixel[VBLUE] = llclampb(pixel[VBLUE] + alpha * blue); break; - case STENCIL_BLEND_MODE_DODGE: - // Dodge/burn the incoming color onto the background image + case STENCIL_BLEND_MODE_ABACK: + // Add back background image to the incoming color pixel[VRED] = llclampb(inv_alpha * pixel[VRED] + red); pixel[VGREEN] = llclampb(inv_alpha * pixel[VGREEN] + green); pixel[VBLUE] = llclampb(inv_alpha * pixel[VBLUE] + blue); -- cgit v1.2.3 From 9112a47f9be023dd83bf4de72d490d21d85e6b5e Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Thu, 6 Feb 2014 10:26:25 -0800 Subject: ACME-1301 : Add convolve as a secondary filter command --- indra/llimage/llimagefilter.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'indra/llimage/llimagefilter.cpp') diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index 7734b08c18..9093e4b59c 100755 --- a/indra/llimage/llimagefilter.cpp +++ b/indra/llimage/llimagefilter.cpp @@ -230,21 +230,32 @@ void LLImageFilter::executeFilter(LLPointer raw_image) else if (filter_name == "sharpen") { LLMatrix3 kernel; - for (S32 i = 0; i < NUM_VALUES_IN_MAT3; i++) + for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++) for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++) - kernel.mMatrix[i][j] = -1.0; + kernel.mMatrix[k][j] = -1.0; kernel.mMatrix[1][1] = 9.0; convolve(kernel,false,false); } else if (filter_name == "gradient") { LLMatrix3 kernel; - for (S32 i = 0; i < NUM_VALUES_IN_MAT3; i++) + for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++) for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++) - kernel.mMatrix[i][j] = -1.0; + kernel.mMatrix[k][j] = -1.0; kernel.mMatrix[1][1] = 8.0; convolve(kernel,false,true); } + else if (filter_name == "convolve") + { + LLMatrix3 kernel; + S32 index = 1; + bool normalize = (mFilterData[i][index++].asReal() > 0.0); + bool abs_value = (mFilterData[i][index++].asReal() > 0.0); + for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++) + for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++) + kernel.mMatrix[k][j] = mFilterData[i][index++].asReal(); + convolve(kernel,normalize,abs_value); + } else { llwarns << "Filter unknown, cannot execute filter command : " << filter_name << llendl; -- cgit v1.2.3 From 2ba7552b9cd94b62c850365bcc537f0b3e344917 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Thu, 6 Feb 2014 11:57:01 -0800 Subject: ACME-1301 : Add colortransform as a secondary filter command --- indra/llimage/llimagefilter.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'indra/llimage/llimagefilter.cpp') diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index 9093e4b59c..8f7e340d16 100755 --- a/indra/llimage/llimagefilter.cpp +++ b/indra/llimage/llimagefilter.cpp @@ -256,6 +256,16 @@ void LLImageFilter::executeFilter(LLPointer raw_image) kernel.mMatrix[k][j] = mFilterData[i][index++].asReal(); convolve(kernel,normalize,abs_value); } + else if (filter_name == "colortransform") + { + LLMatrix3 transform; + S32 index = 1; + for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++) + for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++) + transform.mMatrix[k][j] = mFilterData[i][index++].asReal(); + transform.transpose(); + colorTransform(transform); + } else { llwarns << "Filter unknown, cannot execute filter command : " << filter_name << llendl; -- cgit v1.2.3 From 8ad37cec70be671809f317bbc558eed1b59be595 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Thu, 6 Feb 2014 16:25:12 -0800 Subject: ACME-1278 : Allow angle to be used in 2D screen filter --- indra/llimage/llimagefilter.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'indra/llimage/llimagefilter.cpp') diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index 8f7e340d16..8bd7416f6a 100755 --- a/indra/llimage/llimagefilter.cpp +++ b/indra/llimage/llimagefilter.cpp @@ -510,15 +510,18 @@ void LLImageFilter::filterScreen(EScreenMode mode, const F32 wave_length, const { // Compute screen value F32 value = 0.0; - F32 d = 0.0; + F32 di = 0.0; + F32 dj = 0.0; switch (mode) { case SCREEN_MODE_2DSINE: - value = (sinf(2*F_PI*i/wave_length_pixels)*sinf(2*F_PI*j/wave_length_pixels)+1.0)*255.0/2.0; + di = cos*i + sin*j; + dj = -sin*i + cos*j; + value = (sinf(2*F_PI*di/wave_length_pixels)*sinf(2*F_PI*dj/wave_length_pixels)+1.0)*255.0/2.0; break; case SCREEN_MODE_LINE: - d = sin*i - cos*j; - value = (sinf(2*F_PI*d/wave_length_pixels)+1.0)*255.0/2.0; + dj = sin*i - cos*j; + value = (sinf(2*F_PI*dj/wave_length_pixels)+1.0)*255.0/2.0; break; } U8 dst_value = (dst_data[VRED] >= (U8)(value) ? 255 : 0); -- cgit v1.2.3 From 99427b29ceb8c6a22755075679ae008a37a947ae Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Thu, 6 Feb 2014 18:21:18 -0800 Subject: Clear up comments --- indra/llimage/llimagefilter.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'indra/llimage/llimagefilter.cpp') diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index 8bd7416f6a..a5b5888bb2 100755 --- a/indra/llimage/llimagefilter.cpp +++ b/indra/llimage/llimagefilter.cpp @@ -76,9 +76,7 @@ LLImageFilter::~LLImageFilter() /* *TODO * Rename stencil to mask - * Test blend modes and name them correctly * Improve perf: use LUT for alpha blending in uniform case - * Improve perf: make sure filter is not called more than necessary in viewer (seems to be called 3 times per change) * Add gradient coloring as a filter */ -- cgit v1.2.3 From 73c9e42fafc0ad21af1ed38735c0ed9212640dd2 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Fri, 7 Feb 2014 16:27:42 -0800 Subject: ACME-1314 : Fix screen filter to make it less harsh on edges, modify Jules Verne and Newspaper filters as a result --- indra/llimage/llimagefilter.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'indra/llimage/llimagefilter.cpp') diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index a5b5888bb2..742b0ad802 100755 --- a/indra/llimage/llimagefilter.cpp +++ b/indra/llimage/llimagefilter.cpp @@ -500,6 +500,14 @@ void LLImageFilter::filterScreen(EScreenMode mode, const F32 wave_length, const F32 wave_length_pixels = wave_length * (F32)(height) / 2.0; F32 sin = sinf(angle*DEG_TO_RAD); F32 cos = cosf(angle*DEG_TO_RAD); + + // Precompute the gamma table : gives us the gray level to use when cutting outside the screen (prevents strong aliasing on the screen) + U8 gamma[256]; + for (S32 i = 0; i < 256; i++) + { + F32 gamma_i = llclampf((float)(powf((float)(i)/255.0,1.0/4.0))); + gamma[i] = (U8)(255.0 * gamma_i); + } U8* dst_data = mImage->getData(); for (S32 j = 0; j < height; j++) @@ -522,7 +530,7 @@ void LLImageFilter::filterScreen(EScreenMode mode, const F32 wave_length, const value = (sinf(2*F_PI*dj/wave_length_pixels)+1.0)*255.0/2.0; break; } - U8 dst_value = (dst_data[VRED] >= (U8)(value) ? 255 : 0); + U8 dst_value = (dst_data[VRED] >= (U8)(value) ? gamma[dst_data[VRED] - (U8)(value)] : 0); // Blend result blendStencil(getStencilAlpha(i,j), dst_data, dst_value, dst_value, dst_value); -- cgit v1.2.3 From 51844473c00a7d620746c26af822aa7d41f53832 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Fri, 7 Feb 2014 18:35:46 -0800 Subject: ACME-1315 : Fix anisotropy in filters applied to thumbnail. Deleted some unused code --- indra/llimage/llimagefilter.cpp | 25 ------------------------- 1 file changed, 25 deletions(-) (limited to 'indra/llimage/llimagefilter.cpp') diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index 742b0ad802..3d0c488768 100755 --- a/indra/llimage/llimagefilter.cpp +++ b/indra/llimage/llimagefilter.cpp @@ -542,31 +542,6 @@ void LLImageFilter::filterScreen(EScreenMode mode, const F32 wave_length, const //============================================================================ // Procedural Stencils //============================================================================ - -void LLImageFilter::setStencil(EStencilBlendMode mode, EStencilShape type, F32 gamma, F32 min, F32 max) -{ - 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()); -} - void LLImageFilter::setStencil(EStencilShape shape, EStencilBlendMode mode, F32 min, F32 max, F32* params) { mStencilShape = shape; -- cgit v1.2.3 From a5366f06edf5aa1b75328e26011989eba7c601ca Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Thu, 13 Feb 2014 15:15:05 -0800 Subject: ACME-1277 : Final set of filters, added a SnapshotFiltersEnabled debug setting --- indra/llimage/llimagefilter.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra/llimage/llimagefilter.cpp') diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index 3d0c488768..6e9ea2f948 100755 --- a/indra/llimage/llimagefilter.cpp +++ b/indra/llimage/llimagefilter.cpp @@ -55,6 +55,7 @@ LLImageFilter::LLImageFilter(const std::string& file_path) : { // Load filter description from file llifstream filter_xml(file_path); + llinfos << "Merov : load filter : " << file_path << llendl; if (filter_xml.is_open()) { // Load and parse the file @@ -88,6 +89,7 @@ void LLImageFilter::executeFilter(LLPointer raw_image) { mImage = raw_image; + llinfos << "Merov : execute filter on image size " << mImage->getWidth() << "x" << mImage->getHeight() << llendl; //std::cout << "Filter : size = " << mFilterData.size() << std::endl; for (S32 i = 0; i < mFilterData.size(); ++i) { -- cgit v1.2.3 From fc4efd18479f43766e7945d4ae8caf5e735f6563 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Fri, 14 Feb 2014 09:42:57 -0800 Subject: ACME-1277 : Delete debug printouts spamming the log --- indra/llimage/llimagefilter.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'indra/llimage/llimagefilter.cpp') diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index 6e9ea2f948..3d0c488768 100755 --- a/indra/llimage/llimagefilter.cpp +++ b/indra/llimage/llimagefilter.cpp @@ -55,7 +55,6 @@ LLImageFilter::LLImageFilter(const std::string& file_path) : { // Load filter description from file llifstream filter_xml(file_path); - llinfos << "Merov : load filter : " << file_path << llendl; if (filter_xml.is_open()) { // Load and parse the file @@ -89,7 +88,6 @@ void LLImageFilter::executeFilter(LLPointer raw_image) { mImage = raw_image; - llinfos << "Merov : execute filter on image size " << mImage->getWidth() << "x" << mImage->getHeight() << llendl; //std::cout << "Filter : size = " << mFilterData.size() << std::endl; for (S32 i = 0; i < mFilterData.size(); ++i) { -- cgit v1.2.3