diff options
Diffstat (limited to 'indra')
22 files changed, 956 insertions, 683 deletions
| diff --git a/indra/integration_tests/llimage_libtest/1970colorize.xml b/indra/integration_tests/llimage_libtest/1970colorize.xml new file mode 100644 index 0000000000..0dab2489a0 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/1970colorize.xml @@ -0,0 +1,41 @@ +<llsd> +    <array> +        <array> +            <string>linearize</string> +            <real>0.1</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +        </array> +        <array> +            <string>contrast</string> +            <real>0.8</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +        </array> +        <array> +            <string>colorize</string> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>0.5</real> +            <real>0.0</real> +            <real>0.0</real> +        </array> +        <array> +            <string>blend</string> +            <real>10.0</real> +            <real>0.0</real> +        </array> +        <array> +            <string>colorize</string> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>0.1</real> +            <real>0.1</real> +            <real>0.0</real> +        </array> +    </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/brighten.xml b/indra/integration_tests/llimage_libtest/brighten.xml new file mode 100755 index 0000000000..d17b96d2d7 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/brighten.xml @@ -0,0 +1,11 @@ +<llsd> +    <array> +        <array> +            <string>brighten</string> +            <real>50.0</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +        </array> +    </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/colorize.xml b/indra/integration_tests/llimage_libtest/colorize.xml new file mode 100644 index 0000000000..18c6cd3425 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/colorize.xml @@ -0,0 +1,20 @@ +<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>colorize</string> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>0.2</real> +            <real>0.0</real> +            <real>0.2</real> +        </array> +    </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/contrast.xml b/indra/integration_tests/llimage_libtest/contrast.xml new file mode 100644 index 0000000000..8dcdd1a9a9 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/contrast.xml @@ -0,0 +1,18 @@ +<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>contrast</string> +            <real>1.5</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +        </array> +    </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/darken.xml b/indra/integration_tests/llimage_libtest/darken.xml new file mode 100755 index 0000000000..8d110452e9 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/darken.xml @@ -0,0 +1,11 @@ +<llsd> +    <array> +        <array> +            <string>darken</string> +            <real>50.0</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +        </array> +    </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/gamma.xml b/indra/integration_tests/llimage_libtest/gamma.xml new file mode 100644 index 0000000000..7505a03027 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/gamma.xml @@ -0,0 +1,18 @@ +<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> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/grayscale.xml b/indra/integration_tests/llimage_libtest/grayscale.xml new file mode 100644 index 0000000000..984312c4fd --- /dev/null +++ b/indra/integration_tests/llimage_libtest/grayscale.xml @@ -0,0 +1,14 @@ +<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>grayscale</string> +        </array> +    </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/horizontalscreen.xml b/indra/integration_tests/llimage_libtest/horizontalscreen.xml new file mode 100644 index 0000000000..ddff4d1977 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/horizontalscreen.xml @@ -0,0 +1,25 @@ +<llsd> +    <array> +        <array> +            <string>linearize</string> +            <real>0.1</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +        </array> +        <array> +            <string>grayscale</string> +        </array> +        <array> +            <string>blend</string> +            <real>0.0</real> +            <real>0.0</real> +        </array> +        <array> +            <string>screen</string> +            <string>line</string> +            <real>5.0</real> +            <real>0.0</real> +        </array> +    </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/linearize.xml b/indra/integration_tests/llimage_libtest/linearize.xml new file mode 100755 index 0000000000..23d0290e07 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/linearize.xml @@ -0,0 +1,11 @@ +<llsd> +    <array> +        <array> +            <string>linearize</string> +            <real>0.1</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +        </array> +    </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/newsscreen.xml b/indra/integration_tests/llimage_libtest/newsscreen.xml new file mode 100755 index 0000000000..8247c34500 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/newsscreen.xml @@ -0,0 +1,25 @@ +<llsd> +    <array> +        <array> +            <string>linearize</string> +            <real>0.1</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +        </array> +        <array> +            <string>grayscale</string> +        </array> +        <array> +            <string>blend</string> +            <real>0.0</real> +            <real>0.0</real> +        </array> +        <array> +            <string>screen</string> +            <string>2Dsine</string> +            <real>5.0</real> +            <real>0.0</real> +        </array> +    </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/posterize.xml b/indra/integration_tests/llimage_libtest/posterize.xml new file mode 100755 index 0000000000..f026278f9e --- /dev/null +++ b/indra/integration_tests/llimage_libtest/posterize.xml @@ -0,0 +1,18 @@ +<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>posterize</string> +            <real>10.0</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +        </array> +    </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/rotatecolors180.xml b/indra/integration_tests/llimage_libtest/rotatecolors180.xml new file mode 100644 index 0000000000..e25029720f --- /dev/null +++ b/indra/integration_tests/llimage_libtest/rotatecolors180.xml @@ -0,0 +1,8 @@ +<llsd> +    <array> +        <array> +            <string>rotate</string> +            <real>180.0</real> +        </array> +    </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/saturate.xml b/indra/integration_tests/llimage_libtest/saturate.xml new file mode 100644 index 0000000000..b77f07a037 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/saturate.xml @@ -0,0 +1,8 @@ +<llsd> +    <array> +        <array> +            <string>saturate</string> +            <real>3.0</real> +        </array> +    </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/sepia.xml b/indra/integration_tests/llimage_libtest/sepia.xml new file mode 100644 index 0000000000..0304ead015 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/sepia.xml @@ -0,0 +1,14 @@ +<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>sepia</string> +        </array> +    </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/slantedscreen.xml b/indra/integration_tests/llimage_libtest/slantedscreen.xml new file mode 100644 index 0000000000..63ad01d51d --- /dev/null +++ b/indra/integration_tests/llimage_libtest/slantedscreen.xml @@ -0,0 +1,25 @@ +<llsd> +    <array> +        <array> +            <string>linearize</string> +            <real>0.1</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +        </array> +        <array> +            <string>grayscale</string> +        </array> +        <array> +            <string>blend</string> +            <real>0.0</real> +            <real>0.0</real> +        </array> +        <array> +            <string>screen</string> +            <string>line</string> +            <real>5.0</real> +            <real>45.0</real> +        </array> +    </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/spotlight.xml b/indra/integration_tests/llimage_libtest/spotlight.xml new file mode 100644 index 0000000000..203130bdee --- /dev/null +++ b/indra/integration_tests/llimage_libtest/spotlight.xml @@ -0,0 +1,45 @@ +<llsd> +    <array> +        <array> +            <string>linearize</string> +            <real>0.1</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +        </array> +        <array> +            <string>contrast</string> +            <real>0.8</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +        </array> +        <array> +            <string>saturate</string> +            <real>1.5</real> +        </array> +        <array> +            <string>fade</string> +            <real>1.0</real> +            <real>0.25</real> +        </array> +        <array> +            <string>saturate</string> +            <real>0.8</real> +        </array> +        <array> +            <string>contrast</string> +            <real>1.1</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +        </array> +        <array> +            <string>brighten</string> +            <real>30</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +        </array> +    </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/verticalscreen.xml b/indra/integration_tests/llimage_libtest/verticalscreen.xml new file mode 100644 index 0000000000..71e48df656 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/verticalscreen.xml @@ -0,0 +1,25 @@ +<llsd> +    <array> +        <array> +            <string>linearize</string> +            <real>0.1</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +        </array> +        <array> +            <string>grayscale</string> +        </array> +        <array> +            <string>blend</string> +            <real>0.0</real> +            <real>0.0</real> +        </array> +        <array> +            <string>screen</string> +            <string>line</string> +            <real>5.0</real> +            <real>90.0</real> +        </array> +    </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/video.xml b/indra/integration_tests/llimage_libtest/video.xml new file mode 100755 index 0000000000..8b10687ef5 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/video.xml @@ -0,0 +1,23 @@ +<llsd> +    <array> +        <array> +            <string>linearize</string> +            <real>0.01</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +        </array> +        <array> +            <string>lines</string> +            <real>10.0</real> +            <real>0.0</real> +        </array> +        <array> +            <string>brighten</string> +            <real>100.0</real> +            <real>1.0</real> +            <real>1.0</real> +            <real>1.0</real> +        </array> +    </array> +</llsd> diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index 3105fe3746..7981ca7364 100755 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -98,14 +98,7 @@ LLImageBase::LLImageBase()  	  mHeight(0),  	  mComponents(0),  	  mBadBufferAllocation(false), -	  mAllowOverSize(false), -      mHistoRed(NULL), -      mHistoGreen(NULL), -      mHistoBlue(NULL), -      mHistoBrightness(NULL), -      mVignetteMode(VIGNETTE_MODE_NONE), -      mVignetteGamma(1.0), -      mVignetteMin(0.0) +	  mAllowOverSize(false)  {  } @@ -113,10 +106,6 @@ 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 @@ -947,520 +936,6 @@ 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 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 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)(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 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::filterScreen(EScreenMode mode, const S32 wave_length, const F32 angle) -{ -	const S32 components = getComponents(); -	llassert( components >= 1 && components <= 4 ); -     -	S32 width  = getWidth(); -    S32 height = getHeight(); -     -    F32 sin = sinf(angle*DEG_TO_RAD); -    F32 cos = cosf(angle*DEG_TO_RAD); -     -	U8* dst_data = 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; -        } -	} -} - -void LLImageRaw::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 = getWidth()/2; -    mVignetteCenterY = getHeight()/2; -    mVignetteWidth = llmin(getWidth()/2,getHeight()/2); -} - -F32 LLImageRaw::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)); -} - -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 a600f2e4a6..4b1da233e1 100755 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -88,25 +88,6 @@ 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; - -typedef enum e_vignette_type -{ -	VIGNETTE_TYPE_CENTER = 0, -	VIGNETTE_TYPE_LINES  = 1 -} EVignetteType; - -typedef enum e_screen_mode -{ -	SCREEN_MODE_2DSINE   = 0, -	SCREEN_MODE_LINE     = 1 -} EScreenMode; -  //============================================================================  // library initialization class @@ -171,21 +152,6 @@ 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; -    EVignetteType mVignetteType; -    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); @@ -291,30 +257,6 @@ 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 angle);                   // Rotates hue according to angle, 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 filterScreen(EScreenMode mode, const S32 wave_length, const F32 angle); -    void setVignette(EVignetteMode mode, EVignetteType type, F32 gamma, F32 min); -    U32* getBrightnessHistogram();  protected:  	// Create an image from a local file (generally used in tools) @@ -327,9 +269,6 @@ 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/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 <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" @@ -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 <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" - - // 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<LLImageRaw> 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<LLImageRaw> 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<LLImageRaw> 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); +}  //============================================================================ diff --git a/indra/llimage/llimagefilter.h b/indra/llimage/llimagefilter.h index 5598fa69f5..c67789ede0 100755 --- a/indra/llimage/llimagefilter.h +++ b/indra/llimage/llimagefilter.h @@ -29,7 +29,6 @@  #include "llimage.h" -/*  class LLImageRaw;  class LLColor4U;  class LLColor3; @@ -53,7 +52,7 @@ typedef enum e_screen_mode  	SCREEN_MODE_2DSINE   = 0,  	SCREEN_MODE_LINE     = 1  } EScreenMode; -*/ +  //============================================================================  // Image Filter  @@ -65,8 +64,55 @@ public:      void loadFromFile(const std::string& file_path);      void executeFilter(LLPointer<LLImageRaw> raw_image); +      private: +    // 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 angle);                   // Rotates hue according to angle, 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 filterScreen(EScreenMode mode, const S32 wave_length, const F32 angle); + +    // Procedural Stencils +    void setVignette(EVignetteMode mode, EVignetteType type, F32 gamma, F32 min); +    F32 getVignetteAlpha(S32 i, S32 j); + +    // Histograms +    U32* getBrightnessHistogram(); +    void computeHistograms(); +      LLSD mFilterData; +    LLPointer<LLImageRaw> mImage; + +    // Histograms (if we ever happen to need them) +    U32 *mHistoRed; +    U32 *mHistoGreen; +    U32 *mHistoBlue; +    U32 *mHistoBrightness; +     +    // Vignette filtering +    EVignetteMode mVignetteMode; +    EVignetteType mVignetteType; +    S32 mVignetteCenterX; +    S32 mVignetteCenterY; +    S32 mVignetteWidth; +    F32 mVignetteGamma; +    F32 mVignetteMin;  }; | 
