diff options
-rwxr-xr-x | indra/integration_tests/llimage_libtest/CMakeLists.txt | 3 | ||||
-rwxr-xr-x | indra/integration_tests/llimage_libtest/llimage_libtest.cpp | 262 | ||||
-rwxr-xr-x | indra/llimage/llimage.cpp | 463 | ||||
-rwxr-xr-x | indra/llimage/llimage.h | 49 | ||||
-rwxr-xr-x | indra/newview/app_settings/filters/Gotham.xml | 30 | ||||
-rwxr-xr-x | indra/newview/app_settings/filters/Hefe.xml | 23 | ||||
-rwxr-xr-x | indra/newview/app_settings/filters/Inkwell.xml | 14 | ||||
-rwxr-xr-x | indra/newview/app_settings/filters/Lomofi.xml | 23 | ||||
-rwxr-xr-x | indra/newview/app_settings/filters/Poprocket.xml | 25 | ||||
-rwxr-xr-x | indra/newview/app_settings/filters/Sutro.xml | 19 | ||||
-rwxr-xr-x | indra/newview/app_settings/filters/Toaster.xml | 23 | ||||
-rw-r--r-- | indra/newview/llfloaterflickr.cpp | 32 | ||||
-rw-r--r-- | indra/newview/llfloaterflickr.h | 1 | ||||
-rw-r--r-- | indra/newview/llsnapshotlivepreview.cpp | 16 | ||||
-rw-r--r-- | indra/newview/llsnapshotlivepreview.h | 3 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/panel_flickr_photo.xml | 31 |
16 files changed, 978 insertions, 39 deletions
diff --git a/indra/integration_tests/llimage_libtest/CMakeLists.txt b/indra/integration_tests/llimage_libtest/CMakeLists.txt index 36a7d38bb7..8a83ac498f 100755 --- a/indra/integration_tests/llimage_libtest/CMakeLists.txt +++ b/indra/integration_tests/llimage_libtest/CMakeLists.txt @@ -7,6 +7,7 @@ project (llimage_libtest) include(00-Common) include(LLCommon) include(LLImage) +include(LLMath) include(LLImageJ2COJ) include(LLKDU) include(LLVFS) @@ -15,6 +16,7 @@ include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLVFS_INCLUDE_DIRS} ${LLIMAGE_INCLUDE_DIRS} + ${LLMATH_INCLUDE_DIRS} ) include_directories(SYSTEM ${LLCOMMON_SYSTEM_INCLUDE_DIRS} @@ -64,6 +66,7 @@ endif (DARWIN) target_link_libraries(llimage_libtest ${LLCOMMON_LIBRARIES} ${LLVFS_LIBRARIES} + ${LLMATH_LIBRARIES} ${LLIMAGE_LIBRARIES} ${LLKDU_LIBRARIES} ${KDU_LIBRARY} diff --git a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp index 034c816742..009be0941e 100755 --- a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp +++ b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp @@ -39,6 +39,8 @@ #include "llimagej2c.h" #include "lldir.h" #include "lldiriterator.h" +#include "v4coloru.h" +#include "llsdserialize.h" // system libraries #include <iostream> @@ -83,6 +85,24 @@ static const char USAGE[] = "\n" " -rev, --reversible\n" " Set the compression to be lossless (reversible in j2c parlance).\n" " Only valid for output j2c images.\n" +" -f, --filter <name> [<param>]\n" +" Apply the filter <name> to the input images using the optional <param> value. Admissible names:\n" +" - 'grayscale' converts to grayscale (no param).\n" +" - 'sepia' converts to sepia (no param).\n" +" - 'saturate' changes color saturation according to <param>: < 1.0 will desaturate, > 1.0 will saturate.\n" +" - 'rotate' rotates the color hue according to <param> (in degree, positive value only).\n" +" - 'gamma' applies gamma curve <param> to all channels: > 1.0 will darken, < 1.0 will lighten.\n" +" - 'colorize' applies a red tint to the image using <param> as an alpha (transparency between 0.0 and 1.0) value.\n" +" - 'contrast' modifies the contrast according to <param> : > 1.0 will enhance the contrast, <1.0 will flatten it.\n" +" - 'brighten' adds <param> light to the image (<param> between 0 and 255).\n" +" - 'darken' substracts <param> light to the image (<param> between 0 and 255).\n" +" - 'linearize' optimizes the contrast using the brightness histogram. <param> is the fraction (between 0.0 and 1.0) of discarded tail of the histogram.\n" +" - 'posterize' redistributes the colors between <param> classes per channel (<param> between 2 and 255).\n" +" - Any other value will be interpreted as a file name describing a sequence of filters and parameters to be applied to the input images\n" +" -v, --vignette <name> [<feather> <min>]\n" +" Apply a circular central vignette <name> to the filter using the optional <feather> and <min> values. Admissible names:\n" +" - 'blend' : the filter is applied with full intensity in the center and blends with the image to the periphery.\n" +" - 'fade' : the filter is applied with full intensity in the center and fades to black to the periphery.\n" " -log, --logmetrics <metric>\n" " Log performance data for <metric>. Results in <metric>.slp\n" " Note: so far, only ImageCompressionTester has been tested.\n" @@ -96,10 +116,110 @@ static const char USAGE[] = "\n" // true when all image loading is done. Used by metric logging thread to know when to stop the thread. static bool sAllDone = false; +// Load filter from file +LLSD load_filter_from_file(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 it + LLSD filter_data(LLSD::emptyArray()); + LLPointer<LLSDParser> parser = new LLSDXMLParser(); + parser->parse(filter_xml, filter_data, LLSDSerialize::SIZE_UNLIMITED); + filter_xml.close(); + return filter_data; + } + else + { + return LLSD(); + } +} + +// Apply the filter data to the image passed as parameter +void execute_filter(const LLSD& filter_data, LLPointer<LLImageRaw> raw_image) +{ + //std::cout << "Filter : size = " << filter_data.size() << std::endl; + for (S32 i = 0; i < filter_data.size(); ++i) + { + std::string filter_name = filter_data[i][0].asString(); + // Dump out the filter values (for debug) + //std::cout << "Filter : name = " << filter_data[i][0].asString() << ", params = "; + //for (S32 j = 1; j < filter_data[i].size(); ++j) + //{ + // std::cout << filter_data[i][j].asString() << ", "; + //} + //std::cout << std::endl; + + // Execute the filter described on this line + if (filter_name == "blend") + { + raw_image->setVignette(VIGNETTE_MODE_BLEND,(float)(filter_data[i][1].asReal()),(float)(filter_data[i][2].asReal())); + } + else if (filter_name == "fade") + { + raw_image->setVignette(VIGNETTE_MODE_FADE,(float)(filter_data[i][1].asReal()),(float)(filter_data[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)(filter_data[i][1].asReal())); + } + else if (filter_name == "rotate") + { + raw_image->filterRotate((float)(filter_data[i][1].asReal())); + } + else if (filter_name == "gamma") + { + LLColor3 color((float)(filter_data[i][2].asReal()),(float)(filter_data[i][3].asReal()),(float)(filter_data[i][4].asReal())); + raw_image->filterGamma((float)(filter_data[i][1].asReal()),color); + } + else if (filter_name == "colorize") + { + LLColor3 color((float)(filter_data[i][1].asReal()),(float)(filter_data[i][2].asReal()),(float)(filter_data[i][3].asReal())); + LLColor3 alpha((F32)(filter_data[i][4].asReal()),(float)(filter_data[i][5].asReal()),(float)(filter_data[i][6].asReal())); + raw_image->filterColorize(color,alpha); + } + else if (filter_name == "contrast") + { + LLColor3 color((float)(filter_data[i][2].asReal()),(float)(filter_data[i][3].asReal()),(float)(filter_data[i][4].asReal())); + raw_image->filterContrast((float)(filter_data[i][1].asReal()),color); + } + else if (filter_name == "brighten") + { + LLColor3 color((float)(filter_data[i][2].asReal()),(float)(filter_data[i][3].asReal()),(float)(filter_data[i][4].asReal())); + raw_image->filterBrightness((S32)(filter_data[i][1].asReal()),color); + } + else if (filter_name == "darken") + { + LLColor3 color((float)(filter_data[i][2].asReal()),(float)(filter_data[i][3].asReal()),(float)(filter_data[i][4].asReal())); + raw_image->filterBrightness((S32)(-filter_data[i][1].asReal()),color); + } + else if (filter_name == "linearize") + { + LLColor3 color((float)(filter_data[i][2].asReal()),(float)(filter_data[i][3].asReal()),(float)(filter_data[i][4].asReal())); + raw_image->filterLinearize((float)(filter_data[i][1].asReal()),color); + } + else if (filter_name == "posterize") + { + LLColor3 color((float)(filter_data[i][2].asReal()),(float)(filter_data[i][3].asReal()),(float)(filter_data[i][4].asReal())); + raw_image->filterEqualize((S32)(filter_data[i][1].asReal()),color); + } + } +} + // Create an empty formatted image instance of the correct type from the filename LLPointer<LLImageFormatted> create_image(const std::string &filename) { - std::string exten = gDirUtilp->getExtension(filename); + std::string exten = gDirUtilp->getExtension(filename); LLPointer<LLImageFormatted> image = LLImageFormatted::createFromExtension(exten); return image; } @@ -350,6 +470,11 @@ int main(int argc, char** argv) int blocks_size = -1; int levels = 0; bool reversible = false; + std::string filter_name = ""; + double filter_param = 0.0; + std::string vignette_name = ""; + double vignette_param_1 = 1.0; + double vignette_param_2 = 0.0; // Init whatever is necessary ll_init_apr(); @@ -523,7 +648,73 @@ int main(int argc, char** argv) break; } } - else if (!strcmp(argv[arg], "--analyzeperformance") || !strcmp(argv[arg], "-a")) + else if (!strcmp(argv[arg], "--filter") || !strcmp(argv[arg], "-f")) + { + // '--filter' needs to be specified with a named filter argument + if ((arg + 1) < argc) + { + filter_name = argv[arg+1]; + } + if (((arg + 1) >= argc) || (filter_name[0] == '-')) + { + // We don't have an argument left in the arg list or the next argument is another option + std::cout << "No --filter argument given, no filter will be applied" << std::endl; + } + else + { + arg += 1; // Skip that arg now we know it's a valid test name + if ((arg + 1) == argc) // Break out of the loop if we reach the end of the arg list + break; + // --filter can also have an optional parameter + std::string value_str; + value_str = argv[arg+1]; // Check the next arg + if (value_str[0] != '-') // If it's not another argument, it's a filter parameter value + { + filter_param = atof(value_str.c_str()); + arg += 1; // Skip that arg now we used it as a valid filter param + if ((arg + 1) == argc) // Break out of the loop if we reach the end of the arg list + break; + } + } + } + else if (!strcmp(argv[arg], "--vignette") || !strcmp(argv[arg], "-v")) + { + // '--vignette' needs to be specified with a named vignette argument + if ((arg + 1) < argc) + { + vignette_name = argv[arg+1]; + } + if (((arg + 1) >= argc) || (vignette_name[0] == '-')) + { + // We don't have an argument left in the arg list or the next argument is another option + std::cout << "No --vignette argument given, no vignette will be applied to filters" << std::endl; + } + else + { + arg += 1; // Skip that arg now we know it's a valid vignette name + if ((arg + 1) == argc) // Break out of the loop if we reach the end of the arg list + break; + // --vignette can also have optional parameters + std::string value_str; + value_str = argv[arg+1]; // Check the next arg + if (value_str[0] != '-') // If it's not another argument, it's a vignette parameter value + { + vignette_param_1 = atof(value_str.c_str()); + arg += 1; // Skip that arg now we used it as a valid vignette param + if ((arg + 1) == argc) // Break out of the loop if we reach the end of the arg list + break; + value_str = argv[arg+1]; // Check the next arg + if (value_str[0] != '-') // If it's not another argument, it's a vignette parameter value + { + vignette_param_2 = atof(value_str.c_str()); + arg += 1; // Skip that arg now we used it as a valid vignette param + if ((arg + 1) == argc) // Break out of the loop if we reach the end of the arg list + break; + } + } + } + } + else if (!strcmp(argv[arg], "--analyzeperformance") || !strcmp(argv[arg], "-a")) { analyze_performance = true; } @@ -568,7 +759,72 @@ int main(int argc, char** argv) std::cout << "Error: Image " << *in_file << " could not be loaded" << std::endl; continue; } - + + // Set the vignette if any + if (vignette_name == "blend") + { + raw_image->setVignette(VIGNETTE_MODE_BLEND,(float)(vignette_param_1),(float)(vignette_param_2)); + } + else if (vignette_name == "fade") + { + raw_image->setVignette(VIGNETTE_MODE_FADE,(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 != "") + { + // We're interpreting the filter as a filter file name + LLSD filter_data = load_filter_from_file(filter_name); + execute_filter(filter_data,raw_image); + } + // Save file if (out_file != out_end) { diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index c8a05e1fae..977bb09b63 100755 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -29,7 +29,10 @@ #include "llimage.h" #include "llmath.h" +#include "v3color.h" #include "v4coloru.h" +#include "m3math.h" +#include "v3math.h" #include "llimagebmp.h" #include "llimagetga.h" @@ -95,7 +98,14 @@ LLImageBase::LLImageBase() mHeight(0), mComponents(0), mBadBufferAllocation(false), - mAllowOverSize(false) + mAllowOverSize(false), + mHistoRed(NULL), + mHistoGreen(NULL), + mHistoBlue(NULL), + mHistoBrightness(NULL), + mVignetteMode(VIGNETTE_MODE_NONE), + mVignetteGamma(1.0), + mVignetteMin(0.0) { } @@ -103,10 +113,14 @@ LLImageBase::LLImageBase() LLImageBase::~LLImageBase() { deleteData(); // virtual + ll_aligned_free_16(mHistoRed); + ll_aligned_free_16(mHistoGreen); + ll_aligned_free_16(mHistoBlue); + ll_aligned_free_16(mHistoBrightness); } -//static -void LLImageBase::createPrivatePool() +//static +void LLImageBase::createPrivatePool() { if(!sPrivatePoolp) { @@ -933,6 +947,449 @@ BOOL LLImageRaw::scale( S32 new_width, S32 new_height, BOOL scale_image_data ) return TRUE ; } +// Filter Operations +void LLImageRaw::filterGrayScale() +{ + LLMatrix3 gray_scale; + LLVector3 luminosity(0.2125, 0.7154, 0.0721); + gray_scale.setRows(luminosity, luminosity, luminosity); + gray_scale.transpose(); + colorTransform(gray_scale); +} + +void LLImageRaw::filterSepia() +{ + LLMatrix3 sepia; + sepia.setRows(LLVector3(0.3588, 0.7044, 0.1368), + LLVector3(0.2990, 0.5870, 0.1140), + LLVector3(0.2392, 0.4696, 0.0912)); + sepia.transpose(); + colorTransform(sepia); +} + +void LLImageRaw::filterSaturate(F32 saturation) +{ + // Matrix to Lij + LLMatrix3 r_a; + LLMatrix3 r_b; + + // 45 degre rotation around z + r_a.setRows(LLVector3( OO_SQRT2, OO_SQRT2, 0.0), + LLVector3(-OO_SQRT2, OO_SQRT2, 0.0), + LLVector3( 0.0, 0.0, 1.0)); + // 54.73 degre rotation around y + float oo_sqrt3 = 1.0f / F_SQRT3; + float sin_54 = F_SQRT2 * oo_sqrt3; + r_b.setRows(LLVector3(oo_sqrt3, 0.0, -sin_54), + LLVector3(0.0, 1.0, 0.0), + LLVector3(sin_54, 0.0, oo_sqrt3)); + + // Coordinate conversion + LLMatrix3 Lij = r_b * r_a; + LLMatrix3 Lij_inv = Lij; + Lij_inv.transpose(); + + // Local saturation transform + LLMatrix3 s; + s.setRows(LLVector3(saturation, 0.0, 0.0), + LLVector3(0.0, saturation, 0.0), + LLVector3(0.0, 0.0, 1.0)); + + // Global saturation transform + LLMatrix3 transfo = Lij_inv * s * Lij; + colorTransform(transfo); +} + +void LLImageRaw::filterRotate(F32 alpha) +{ + // 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; + alpha *= DEG_TO_RAD; + r.setRows(LLVector3( cosf(alpha), sinf(alpha), 0.0), + LLVector3(-sinf(alpha), cosf(alpha), 0.0), + LLVector3( 0.0, 0.0, 1.0)); + + // Global color rotation transform + LLMatrix3 transfo = Lij_inv * r * Lij; + colorTransform(transfo); +} + +void LLImageRaw::filterGamma(F32 gamma, const LLColor3& alpha) +{ + U8 gamma_red_lut[256]; + U8 gamma_green_lut[256]; + U8 gamma_blue_lut[256]; + + for (S32 i = 0; i < 256; i++) + { + F32 gamma_i = llclampf((float)(pow((float)(i)/255.0,gamma))); + // Blend in with alpha values + gamma_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * 255.0 * gamma_i); + gamma_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * 255.0 * gamma_i); + gamma_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * 255.0 * gamma_i); + } + + colorCorrect(gamma_red_lut,gamma_green_lut,gamma_blue_lut); +} + +void LLImageRaw::filterLinearize(F32 tail, const LLColor3& alpha) +{ + // Get the histogram + U32* histo = getBrightnessHistogram(); + + // Compute cumulated histogram + U32 cumulated_histo[256]; + cumulated_histo[0] = histo[0]; + for (S32 i = 1; i < 256; i++) + { + cumulated_histo[i] = cumulated_histo[i-1] + histo[i]; + } + + // Compute min and max counts minus tail + tail = llclampf(tail); + S32 total = cumulated_histo[255]; + S32 min_c = (S32)((F32)(total) * tail); + S32 max_c = (S32)((F32)(total) * (1.0 - tail)); + + // Find min and max values + S32 min_v = 0; + while (cumulated_histo[min_v] < min_c) + { + min_v++; + } + S32 max_v = 255; + while (cumulated_histo[max_v] > max_c) + { + max_v--; + } + + // Compute linear lookup table + U8 linear_red_lut[256]; + U8 linear_green_lut[256]; + U8 linear_blue_lut[256]; + if (max_v == min_v) + { + // Degenerated binary split case + for (S32 i = 0; i < 256; i++) + { + U8 value_i = (i < min_v ? 0 : 255); + // Blend in with alpha values + linear_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i); + linear_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i); + linear_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i); + } + } + else + { + // Linearize between min and max + F32 slope = 255.0 / (F32)(max_v - min_v); + F32 translate = -min_v * slope; + for (S32 i = 0; i < 256; i++) + { + U8 value_i = (U8)(llclampb((S32)(slope*i + translate))); + // Blend in with alpha values + linear_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i); + linear_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i); + linear_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i); + } + } + + // Apply lookup table + colorCorrect(linear_red_lut,linear_green_lut,linear_blue_lut); +} + +void LLImageRaw::filterEqualize(S32 nb_classes, const LLColor3& alpha) +{ + // Regularize the parameter: must be between 2 and 255 + nb_classes = llmax(nb_classes,2); + nb_classes = llclampb(nb_classes); + + // Get the histogram + U32* histo = getBrightnessHistogram(); + + // Compute cumulated histogram + U32 cumulated_histo[256]; + cumulated_histo[0] = histo[0]; + for (S32 i = 1; i < 256; i++) + { + cumulated_histo[i] = cumulated_histo[i-1] + histo[i]; + } + + // Compute deltas + S32 total = cumulated_histo[255]; + S32 delta_count = total / nb_classes; + S32 current_count = delta_count; + S32 delta_value = 256 / (nb_classes - 1); + S32 current_value = 0; + + // Compute equalized lookup table + U8 equalize_red_lut[256]; + U8 equalize_green_lut[256]; + U8 equalize_blue_lut[256]; + for (S32 i = 0; i < 256; i++) + { + // Blend in current_value with alpha values + equalize_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * current_value); + equalize_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * current_value); + equalize_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * current_value); + if (cumulated_histo[i] >= current_count) + { + current_count += delta_count; + current_value += delta_value; + current_value = llclampb(current_value); + } + } + + // Apply lookup table + colorCorrect(equalize_red_lut,equalize_green_lut,equalize_blue_lut); +} + +void LLImageRaw::filterColorize(const LLColor3& color, const LLColor3& alpha) +{ + U8 red_lut[256]; + U8 green_lut[256]; + U8 blue_lut[256]; + + F32 red_composite = 255.0 * alpha.mV[0] * color.mV[0]; + F32 green_composite = 255.0 * alpha.mV[1] * color.mV[1]; + F32 blue_composite = 255.0 * alpha.mV[2] * color.mV[2]; + + for (S32 i = 0; i < 256; i++) + { + red_lut[i] = (U8)(llclampb((S32)((1.0 - alpha.mV[0]) * (F32)(i) + red_composite))); + green_lut[i] = (U8)(llclampb((S32)((1.0 - alpha.mV[1]) * (F32)(i) + green_composite))); + blue_lut[i] = (U8)(llclampb((S32)((1.0 - alpha.mV[2]) * (F32)(i) + blue_composite))); + } + + colorCorrect(red_lut,green_lut,blue_lut); +} + +void LLImageRaw::filterContrast(F32 slope, const LLColor3& alpha) +{ + U8 contrast_red_lut[256]; + U8 contrast_green_lut[256]; + U8 contrast_blue_lut[256]; + + F32 translate = 128.0 * (1.0 - slope); + + for (S32 i = 0; i < 256; i++) + { + U8 value_i = (U8)(llclampb((S32)(slope*i + translate))); + // Blend in with alpha values + contrast_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i); + contrast_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i); + contrast_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i); + } + + colorCorrect(contrast_red_lut,contrast_green_lut,contrast_blue_lut); +} + +void LLImageRaw::filterBrightness(S32 add, const LLColor3& alpha) +{ + U8 brightness_red_lut[256]; + U8 brightness_green_lut[256]; + U8 brightness_blue_lut[256]; + + for (S32 i = 0; i < 256; i++) + { + U8 value_i = (U8)(llclampb((S32)((S32)(i) + add))); + // Blend in with alpha values + brightness_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i); + brightness_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i); + brightness_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i); + } + + colorCorrect(brightness_red_lut,brightness_green_lut,brightness_blue_lut); +} + +// Filter Primitives +void LLImageRaw::colorTransform(const LLMatrix3 &transform) +{ + const S32 components = getComponents(); + llassert( components >= 1 && components <= 4 ); + + S32 width = getWidth(); + S32 height = getHeight(); + + U8* dst_data = getData(); + for (S32 j = 0; j < height; j++) + { + for (S32 i = 0; i < width; i++) + { + LLVector3 src((F32)(dst_data[VRED]),(F32)(dst_data[VGREEN]),(F32)(dst_data[VBLUE])); + LLVector3 dst = src * transform; + dst.clamp(0.0f,255.0f); + if (mVignetteMode == VIGNETTE_MODE_NONE) + { + dst_data[VRED] = dst.mV[VRED]; + dst_data[VGREEN] = dst.mV[VGREEN]; + dst_data[VBLUE] = dst.mV[VBLUE]; + } + else + { + F32 alpha = getVignetteAlpha(i,j); + if (mVignetteMode == VIGNETTE_MODE_BLEND) + { + // Blends with the source image on the edges + F32 inv_alpha = 1.0 - alpha; + dst_data[VRED] = inv_alpha * src.mV[VRED] + alpha * dst.mV[VRED]; + dst_data[VGREEN] = inv_alpha * src.mV[VGREEN] + alpha * dst.mV[VGREEN]; + dst_data[VBLUE] = inv_alpha * src.mV[VBLUE] + alpha * dst.mV[VBLUE]; + } + else // VIGNETTE_MODE_FADE + { + // Fade to black on the edges + dst_data[VRED] = alpha * dst.mV[VRED]; + dst_data[VGREEN] = alpha * dst.mV[VGREEN]; + dst_data[VBLUE] = alpha * dst.mV[VBLUE]; + } + } + dst_data += components; + } + } +} + +void LLImageRaw::colorCorrect(const U8* lut_red, const U8* lut_green, const U8* lut_blue) +{ + const S32 components = getComponents(); + llassert( components >= 1 && components <= 4 ); + + S32 width = getWidth(); + S32 height = getHeight(); + + U8* dst_data = getData(); + for (S32 j = 0; j < height; j++) + { + for (S32 i = 0; i < width; i++) + { + if (mVignetteMode == VIGNETTE_MODE_NONE) + { + dst_data[VRED] = lut_red[dst_data[VRED]]; + dst_data[VGREEN] = lut_green[dst_data[VGREEN]]; + dst_data[VBLUE] = lut_blue[dst_data[VBLUE]]; + } + else + { + F32 alpha = getVignetteAlpha(i,j); + if (mVignetteMode == VIGNETTE_MODE_BLEND) + { + // Blends with the source image on the edges + F32 inv_alpha = 1.0 - alpha; + dst_data[VRED] = inv_alpha * dst_data[VRED] + alpha * lut_red[dst_data[VRED]]; + dst_data[VGREEN] = inv_alpha * dst_data[VGREEN] + alpha * lut_green[dst_data[VGREEN]]; + dst_data[VBLUE] = inv_alpha * dst_data[VBLUE] + alpha * lut_blue[dst_data[VBLUE]]; + } + else // VIGNETTE_MODE_FADE + { + // Fade to black on the edges + dst_data[VRED] = alpha * lut_red[dst_data[VRED]]; + dst_data[VGREEN] = alpha * lut_green[dst_data[VGREEN]]; + dst_data[VBLUE] = alpha * lut_blue[dst_data[VBLUE]]; + } + } + dst_data += components; + } + } +} + +void LLImageRaw::setVignette(EVignetteMode mode, F32 gamma, F32 min) +{ + mVignetteMode = mode; + mVignetteGamma = gamma; + mVignetteMin = llclampf(min); + // We always center the vignette on the image and fits it in the image smallest dimension + mVignetteCenterX = getWidth()/2; + mVignetteCenterY = getHeight()/2; + mVignetteWidth = llmin(getWidth()/2,getHeight()/2); +} + +F32 LLImageRaw::getVignetteAlpha(S32 i, S32 j) +{ + // 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); + F32 alpha = powf(F_E, -(powf((d_center_square/(mVignetteWidth*mVignetteWidth)),mVignetteGamma)/2.0f)); + // We rescale alpha between min and 1.0 so to avoid complete fading if so desired. + return (mVignetteMin + alpha * (1.0 - mVignetteMin)); +} + +U32* LLImageRaw::getBrightnessHistogram() +{ + if (!mHistoBrightness) + { + computeHistograms(); + } + return mHistoBrightness; +} + +void LLImageRaw::computeHistograms() +{ + const S32 components = getComponents(); + llassert( components >= 1 && components <= 4 ); + + // Allocate memory for the histograms + if (!mHistoRed) + { + mHistoRed = (U32*) ll_aligned_malloc_16(256*sizeof(U32)); + } + if (!mHistoGreen) + { + mHistoGreen = (U32*) ll_aligned_malloc_16(256*sizeof(U32)); + } + if (!mHistoBlue) + { + mHistoBlue = (U32*) ll_aligned_malloc_16(256*sizeof(U32)); + } + if (!mHistoBrightness) + { + mHistoBrightness = (U32*) ll_aligned_malloc_16(256*sizeof(U32)); + } + + // Initialize them + for (S32 i = 0; i < 256; i++) + { + mHistoRed[i] = 0; + mHistoGreen[i] = 0; + mHistoBlue[i] = 0; + mHistoBrightness[i] = 0; + } + + // Compute them + S32 pixels = getWidth() * getHeight(); + U8* dst_data = getData(); + for (S32 i = 0; i < pixels; i++) + { + mHistoRed[dst_data[VRED]]++; + mHistoGreen[dst_data[VGREEN]]++; + mHistoBlue[dst_data[VBLUE]]++; + // Note: this is a very simple shorthand for brightness but it's OK for our use + S32 brightness = ((S32)(dst_data[VRED]) + (S32)(dst_data[VGREEN]) + (S32)(dst_data[VBLUE])) / 3; + mHistoBrightness[brightness]++; + // next pixel... + dst_data += components; + } +} + void LLImageRaw::copyLineScaled( U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len, S32 in_pixel_step, S32 out_pixel_step ) { const S32 components = getComponents(); diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index 2277afc585..cc91f95624 100755 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -71,6 +71,8 @@ const S32 HTTP_PACKET_SIZE = 1496; class LLImageFormatted; class LLImageRaw; class LLColor4U; +class LLColor3; +class LLMatrix3; class LLPrivateMemoryPool; typedef enum e_image_codec @@ -86,6 +88,13 @@ typedef enum e_image_codec IMG_CODEC_EOF = 8 } EImageCodec; +typedef enum e_vignette_mode +{ + VIGNETTE_MODE_NONE = 0, + VIGNETTE_MODE_BLEND = 1, + VIGNETTE_MODE_FADE = 2 +} EVignetteMode; + //============================================================================ // library initialization class @@ -150,6 +159,20 @@ protected: // special accessor to allow direct setting of mData and mDataSize by LLImageFormatted void setDataAndSize(U8 *data, S32 size); + // Histograms (if we ever happen to need them) + U32 *mHistoRed; + U32 *mHistoGreen; + U32 *mHistoBlue; + U32 *mHistoBrightness; + + // Vignette filtering + EVignetteMode mVignetteMode; + S32 mVignetteCenterX; + S32 mVignetteCenterY; + S32 mVignetteWidth; + F32 mVignetteGamma; + F32 mVignetteMin; + public: static void generateMip(const U8 *indata, U8* mipdata, int width, int height, S32 nchannels); @@ -255,6 +278,29 @@ public: // Src and dst are same size. Src has 4 components. Dst has 3 components. void compositeUnscaled4onto3( LLImageRaw* src ); + + // Filter Operations : Transforms + void filterGrayScale(); // Convert to grayscale + void filterSepia(); // Convert to sepia + void filterSaturate(F32 saturation); // < 1.0 desaturates, > 1.0 saturates + void filterRotate(F32 alpha); // Rotates hue according to alpha, alpha is an angle in degrees + + // Filter Operations : Color Corrections + // When specified, the LLColor3 alpha parameter indicates the intensity of the effect for each color channel + // acting in effect as an alpha blending factor different for each channel. For instance (1.0,0.0,0.0) will apply + // the effect only to the Red channel. Intermediate values blends the effect with the source color. + void filterGamma(F32 gamma, const LLColor3& alpha); // Apply gamma to each channel + void filterLinearize(F32 tail, const LLColor3& alpha); // Use histogram to linearize constrast between min and max values minus tail + void filterEqualize(S32 nb_classes, const LLColor3& alpha); // Use histogram to equalize constrast between nb_classes throughout the image + void filterColorize(const LLColor3& color, const LLColor3& alpha); // Colorize with color and alpha per channel + void filterContrast(F32 slope, const LLColor3& alpha); // Change contrast according to slope: > 1.0 more contrast, < 1.0 less contrast + void filterBrightness(S32 add, const LLColor3& alpha); // Change brightness according to add: > 0 brighter, < 0 darker + + // Filter Primitives + void colorTransform(const LLMatrix3 &transform); + void colorCorrect(const U8* lut_red, const U8* lut_green, const U8* lut_blue); + void setVignette(EVignetteMode mode, F32 gamma, F32 min); + U32* getBrightnessHistogram(); protected: // Create an image from a local file (generally used in tools) @@ -267,6 +313,9 @@ protected: void setDataAndSize(U8 *data, S32 width, S32 height, S8 components) ; + void computeHistograms(); + F32 getVignetteAlpha(S32 i, S32 j); + public: static S32 sGlobalRawMemory; static S32 sRawImageCount; diff --git a/indra/newview/app_settings/filters/Gotham.xml b/indra/newview/app_settings/filters/Gotham.xml new file mode 100755 index 0000000000..eb0725e6bf --- /dev/null +++ b/indra/newview/app_settings/filters/Gotham.xml @@ -0,0 +1,30 @@ +<llsd> + <array> + <array> + <string>linearize</string> + <real>0.0</real> + <real>1.0</real> + <real>1.0</real> + <real>1.0</real> + </array> + <array> + <string>gamma</string> + <real>1.5</real> + <real>1.0</real> + <real>1.0</real> + <real>1.0</real> + </array> + <array> + <string>colorize</string> + <real>0.0</real> + <real>0.0</real> + <real>0.0</real> + <real>0.0</real> + <real>0.0</real> + <real>1.0</real> + </array> + <array> + <string>grayscale</string> + </array> + </array> +</llsd> diff --git a/indra/newview/app_settings/filters/Hefe.xml b/indra/newview/app_settings/filters/Hefe.xml new file mode 100755 index 0000000000..527aaee847 --- /dev/null +++ b/indra/newview/app_settings/filters/Hefe.xml @@ -0,0 +1,23 @@ +<llsd> + <array> + <array> + <string>linearize</string> + <real>0.0</real> + <real>1.0</real> + <real>1.0</real> + <real>1.0</real> + </array> + <array> + <string>blend</string> + <real>4.0</real> + <real>0.5</real> + </array> + <array> + <string>contrast</string> + <real>2.0</real> + <real>1.0</real> + <real>1.0</real> + <real>1.0</real> + </array> + </array> +</llsd> diff --git a/indra/newview/app_settings/filters/Inkwell.xml b/indra/newview/app_settings/filters/Inkwell.xml new file mode 100755 index 0000000000..77c88b5fbb --- /dev/null +++ b/indra/newview/app_settings/filters/Inkwell.xml @@ -0,0 +1,14 @@ +<llsd> + <array> + <array> + <string>linearize</string> + <real>0.05</real> + <real>1.0</real> + <real>1.0</real> + <real>1.0</real> + </array> + <array> + <string>grayscale</string> + </array> + </array> +</llsd> diff --git a/indra/newview/app_settings/filters/Lomofi.xml b/indra/newview/app_settings/filters/Lomofi.xml new file mode 100755 index 0000000000..f5ec911e59 --- /dev/null +++ b/indra/newview/app_settings/filters/Lomofi.xml @@ -0,0 +1,23 @@ +<llsd> + <array> + <array> + <string>blend</string> + <real>4.0</real> + <real>0.0</real> + </array> + <array> + <string>linearize</string> + <real>0.2</real> + <real>1.0</real> + <real>1.0</real> + <real>1.0</real> + </array> + <array> + <string>brighten</string> + <real>20.0</real> + <real>1.0</real> + <real>1.0</real> + <real>1.0</real> + </array> + </array> +</llsd> diff --git a/indra/newview/app_settings/filters/Poprocket.xml b/indra/newview/app_settings/filters/Poprocket.xml new file mode 100755 index 0000000000..7e64003908 --- /dev/null +++ b/indra/newview/app_settings/filters/Poprocket.xml @@ -0,0 +1,25 @@ +<llsd> + <array> + <array> + <string>linearize</string> + <real>0.0</real> + <real>1.0</real> + <real>1.0</real> + <real>1.0</real> + </array> + <array> + <string>fade</string> + <real>4.0</real> + <real>0.5</real> + </array> + <array> + <string>colorize</string> + <real>1.0</real> + <real>0.0</real> + <real>0.0</real> + <real>0.4</real> + <real>0.0</real> + <real>0.0</real> + </array> + </array> +</llsd> diff --git a/indra/newview/app_settings/filters/Sutro.xml b/indra/newview/app_settings/filters/Sutro.xml new file mode 100755 index 0000000000..3b0a58f01e --- /dev/null +++ b/indra/newview/app_settings/filters/Sutro.xml @@ -0,0 +1,19 @@ +<llsd> + <array> + <array> + <string>linearize</string> + <real>0.05</real> + <real>1.0</real> + <real>1.0</real> + <real>1.0</real> + </array> + <array> + <string>fade</string> + <real>4.0</real> + <real>0.5</real> + </array> + <array> + <string>sepia</string> + </array> + </array> +</llsd> diff --git a/indra/newview/app_settings/filters/Toaster.xml b/indra/newview/app_settings/filters/Toaster.xml new file mode 100755 index 0000000000..170a1183ed --- /dev/null +++ b/indra/newview/app_settings/filters/Toaster.xml @@ -0,0 +1,23 @@ +<llsd> + <array> + <array> + <string>contrast</string> + <real>0.8</real> + <real>1.0</real> + <real>1.0</real> + <real>1.0</real> + </array> + <array> + <string>fade</string> + <real>4.0</real> + <real>0.5</real> + </array> + <array> + <string>brighten</string> + <real>10.0</real> + <real>1.0</real> + <real>1.0</real> + <real>1.0</real> + </array> + </array> +</llsd> diff --git a/indra/newview/llfloaterflickr.cpp b/indra/newview/llfloaterflickr.cpp index 0a4c3f091b..675266143d 100644 --- a/indra/newview/llfloaterflickr.cpp +++ b/indra/newview/llfloaterflickr.cpp @@ -92,6 +92,8 @@ BOOL LLFlickrPhotoPanel::postBuild() mSnapshotPanel = getChild<LLUICtrl>("snapshot_panel"); mResolutionComboBox = getChild<LLUICtrl>("resolution_combobox"); mResolutionComboBox->setCommitCallback(boost::bind(&LLFlickrPhotoPanel::updateResolution, this, TRUE)); + mFilterComboBox = getChild<LLUICtrl>("filters_combobox"); + mFilterComboBox->setCommitCallback(boost::bind(&LLFlickrPhotoPanel::updateResolution, this, TRUE)); mRefreshBtn = getChild<LLUICtrl>("new_snapshot_btn"); mWorkingLabel = getChild<LLUICtrl>("working_lbl"); mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder"); @@ -301,33 +303,18 @@ void LLFlickrPhotoPanel::clearAndClose() void LLFlickrPhotoPanel::updateControls() { LLSnapshotLivePreview* previewp = getPreviewView(); - BOOL got_bytes = previewp && previewp->getDataSize() > 0; BOOL got_snap = previewp && previewp->getSnapshotUpToDate(); - LLSnapshotLivePreview::ESnapshotType shot_type = (previewp ? previewp->getSnapshotType() : LLSnapshotLivePreview::SNAPSHOT_POSTCARD); // *TODO: Separate maximum size for Web images from postcards lldebugs << "Is snapshot up-to-date? " << got_snap << llendl; - LLLocale locale(LLLocale::USER_LOCALE); - std::string bytes_string; - if (got_snap) - { - LLResMgr::getInstance()->getIntegerString(bytes_string, (previewp->getDataSize()) >> 10 ); - } - - //getChild<LLUICtrl>("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : getString("unknown")); <---uses localized string - getChild<LLUICtrl>("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : "unknown"); - getChild<LLUICtrl>("file_size_label")->setColor( - shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD - && got_bytes - && previewp->getDataSize() > MAX_POSTCARD_DATASIZE ? LLUIColor(LLColor4::red) : LLUIColorTable::instance().getColor( "LabelTextColor" )); - updateResolution(FALSE); } void LLFlickrPhotoPanel::updateResolution(BOOL do_update) { - LLComboBox* combobox = static_cast<LLComboBox *>(mResolutionComboBox); + LLComboBox* combobox = static_cast<LLComboBox *>(mResolutionComboBox); + LLComboBox* filterbox = static_cast<LLComboBox *>(mFilterComboBox); std::string sdstring = combobox->getSelectedValue(); LLSD sdres; @@ -336,6 +323,9 @@ void LLFlickrPhotoPanel::updateResolution(BOOL do_update) S32 width = sdres[0]; S32 height = sdres[1]; + + const std::string& filter_name = filterbox->getSimple(); + llinfos << "Merov : filter name is : " << filter_name << llendl; LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get()); if (previewp && combobox->getCurrentIndex() >= 0) @@ -359,10 +349,16 @@ void LLFlickrPhotoPanel::updateResolution(BOOL do_update) checkAspectRatio(width); previewp->getSize(width, height); + // Merov : + // Get the old filter, compare to the current one "filter_name" and set if changed + // If changed, also force the updateSnapshot() here under + S32 original_filter = previewp->getFilter(); + S32 filter = ("Gray Scale" == filter_name ? 1 : 0); - if(original_width != width || original_height != height) + if ((original_width != width) || (original_height != height) || (original_filter != filter)) { previewp->setSize(width, height); + previewp->setFilter(filter); // hide old preview as the aspect ratio could be wrong lldebugs << "updating thumbnail" << llendl; diff --git a/indra/newview/llfloaterflickr.h b/indra/newview/llfloaterflickr.h index 9a329d4451..1d9e649899 100644 --- a/indra/newview/llfloaterflickr.h +++ b/indra/newview/llfloaterflickr.h @@ -63,6 +63,7 @@ private: LLUICtrl * mSnapshotPanel; LLUICtrl * mResolutionComboBox; + LLUICtrl * mFilterComboBox; LLUICtrl * mRefreshBtn; LLUICtrl * mWorkingLabel; LLUICtrl * mThumbnailPlaceholder; diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index 7f578975db..2931178ace 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -89,7 +89,8 @@ LLSnapshotLivePreview::LLSnapshotLivePreview (const LLSnapshotLivePreview::Param mCameraPos(LLViewerCamera::getInstance()->getOrigin()), mCameraRot(LLViewerCamera::getInstance()->getQuaternion()), mSnapshotActive(FALSE), - mSnapshotBufferType(LLViewerWindow::SNAPSHOT_TYPE_COLOR) + mSnapshotBufferType(LLViewerWindow::SNAPSHOT_TYPE_COLOR), + mFilterType(0) { setSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")); mSnapshotDelayTimer.setTimerExpirySec(0.0f); @@ -585,7 +586,12 @@ void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update) if(raw) { raw->expandToPowerOfTwo(); - mThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE); + // Merov : Filter also the thumbnail? + if (getFilter() == 1) + { + raw->filterGrayScale(); + } + mThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE); mThumbnailUpToDate = TRUE ; } @@ -689,6 +695,12 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) } else { + // Merov : Time to apply the filter to mPreviewImage!!! + if (previewp->getFilter() == 1) + { + previewp->mPreviewImage->filterGrayScale(); + } + // delete any existing image previewp->mFormattedImage = NULL; // now create the new one of the appropriate format. diff --git a/indra/newview/llsnapshotlivepreview.h b/indra/newview/llsnapshotlivepreview.h index 0c63bf47c7..d5ae3b491b 100644 --- a/indra/newview/llsnapshotlivepreview.h +++ b/indra/newview/llsnapshotlivepreview.h @@ -95,6 +95,8 @@ public: void setSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat type) { mSnapshotFormat = type; } bool setSnapshotQuality(S32 quality, bool set_by_user = true); void setSnapshotBufferType(LLViewerWindow::ESnapshotType type) { mSnapshotBufferType = type; } + void setFilter(S32 filter) { mFilterType = filter; } + S32 getFilter() { return mFilterType; } void updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail = FALSE, F32 delay = 0.f); void saveWeb(); void saveTexture(); @@ -154,6 +156,7 @@ private: LLQuaternion mCameraRot; BOOL mSnapshotActive; LLViewerWindow::ESnapshotType mSnapshotBufferType; + S32 mFilterType; // *TODO: eventually use a string and a named filter public: static std::set<LLSnapshotLivePreview*> sList; diff --git a/indra/newview/skins/default/xui/en/panel_flickr_photo.xml b/indra/newview/skins/default/xui/en/panel_flickr_photo.xml index b3af271f34..28e3557e15 100644 --- a/indra/newview/skins/default/xui/en/panel_flickr_photo.xml +++ b/indra/newview/skins/default/xui/en/panel_flickr_photo.xml @@ -40,19 +40,24 @@ name="1024x768" value="[i1024,i768]" /> </combo_box> - <text - follows="left|top" - font="SansSerifSmall" - height="14" - left="208" - length="1" - halign="right" - name="file_size_label" - top="9" - type="string" - width="50"> - [SIZE] KB - </text> + <combo_box + control_name="SocialPhotoFilters" + follows="right|top" + name="filters_combobox" + tool_tip="Image filters" + top="6" + left="165" + height="21" + width="135"> + <combo_box.item + label="No Filter" + name="NoFilter" + value="NoFilter" /> + <combo_box.item + label="Gray Scale" + name="GrayScale" + value="GrayScale" /> + </combo_box> <panel height="150" width="250" |