summaryrefslogtreecommitdiffstats
path: root/src/libs/levels/imagelevels.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/levels/imagelevels.cpp')
-rw-r--r--src/libs/levels/imagelevels.cpp715
1 files changed, 715 insertions, 0 deletions
diff --git a/src/libs/levels/imagelevels.cpp b/src/libs/levels/imagelevels.cpp
new file mode 100644
index 00000000..ac995927
--- /dev/null
+++ b/src/libs/levels/imagelevels.cpp
@@ -0,0 +1,715 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2004-07-29
+ * Description : image levels manipulation methods.
+ *
+ * Copyright (C) 2004-2008 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * Some code parts are inspired from gimp 2.0
+ * app/base/levels.c, gimplut.c, and app/base/gimpleveltool.c
+ * source files.
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation;
+ * either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * ============================================================ */
+
+// TQt includes.
+
+#include <tqfile.h>
+
+// C++ includes.
+
+#include <cstdio>
+#include <cmath>
+#include <cstring>
+#include <cstdlib>
+#include <cerrno>
+
+// Local includes.
+
+#include "ddebug.h"
+#include "imagehistogram.h"
+#include "imagelevels.h"
+
+namespace Digikam
+{
+
+class ImageLevelsPriv
+{
+
+public:
+
+ enum PixelType
+ {
+ RedPixel = 0,
+ GreenPixel,
+ BluePixel,
+ AlphaPixel
+ };
+
+ struct _Levels
+ {
+ double gamma[5];
+
+ int low_input[5];
+ int high_input[5];
+
+ int low_output[5];
+ int high_output[5];
+ };
+
+ struct _Lut
+ {
+ unsigned short **luts;
+ int nchannels;
+ };
+
+public:
+
+ ImageLevelsPriv()
+ {
+ levels = 0;
+ lut = 0;
+ dirty = false;
+ }
+
+ // Levels data.
+ struct _Levels *levels;
+
+ // Lut data.
+ struct _Lut *lut;
+
+ bool sixteenBit;
+ bool dirty;
+};
+
+ImageLevels::ImageLevels(bool sixteenBit)
+{
+ d = new ImageLevelsPriv;
+ d->lut = new ImageLevelsPriv::_Lut;
+ d->levels = new ImageLevelsPriv::_Levels;
+ d->sixteenBit = sixteenBit;
+
+ memset(d->levels, 0, sizeof(struct ImageLevelsPriv::_Levels));
+ d->lut->luts = NULL;
+ d->lut->nchannels = 0;
+
+ reset();
+}
+
+ImageLevels::~ImageLevels()
+{
+ if (d->lut)
+ {
+ if (d->lut->luts)
+ {
+ for (int i = 0 ; i < d->lut->nchannels ; i++)
+ delete [] d->lut->luts[i];
+
+ delete [] d->lut->luts;
+ }
+
+ delete d->lut;
+ }
+
+ if (d->levels)
+ delete d->levels;
+
+ delete d;
+}
+
+bool ImageLevels::isDirty()
+{
+ return d->dirty;
+}
+
+bool ImageLevels::isSixteenBits()
+{
+ return d->sixteenBit;
+}
+
+void ImageLevels::reset()
+{
+ for (int channel = 0 ; channel < 5 ; channel++)
+ levelsChannelReset(channel);
+}
+
+void ImageLevels::levelsChannelReset(int channel)
+{
+ if (!d->levels) return;
+
+ d->levels->gamma[channel] = 1.0;
+ d->levels->low_input[channel] = 0;
+ d->levels->high_input[channel] = d->sixteenBit ? 65535 : 255;
+ d->levels->low_output[channel] = 0;
+ d->levels->high_output[channel] = d->sixteenBit ? 65535 : 255;
+ d->dirty = false;
+}
+
+void ImageLevels::levelsAuto(ImageHistogram *hist)
+{
+ if (!d->levels || !hist) return;
+
+ levelsChannelReset(ImageHistogram::ValueChannel);
+
+ for (int channel = ImageHistogram::RedChannel ;
+ channel <= ImageHistogram::BlueChannel ;
+ channel++)
+ {
+ levelsChannelAuto(hist, channel);
+ }
+ d->dirty = true;
+}
+
+void ImageLevels::levelsChannelAuto(ImageHistogram *hist, int channel)
+{
+ int i;
+ double count, new_count, percentage, next_percentage;
+
+ if (!d->levels || !hist) return;
+
+ d->levels->gamma[channel] = 1.0;
+ d->levels->low_output[channel] = 0;
+ d->levels->high_output[channel] = d->sixteenBit ? 65535 : 255;
+
+ count = hist->getCount(channel, 0, d->sixteenBit ? 65535 : 255);
+
+ if (count == 0.0)
+ {
+ d->levels->low_input[channel] = 0;
+ d->levels->high_input[channel] = 0;
+ }
+ else
+ {
+ // Set the low input
+
+ new_count = 0.0;
+
+ for (i = 0 ; i < (d->sixteenBit ? 65535 : 255) ; i++)
+ {
+ new_count += hist->getValue(channel, i);
+ percentage = new_count / count;
+ next_percentage = (new_count + hist->getValue(channel, i + 1)) / count;
+
+ if (fabs (percentage - 0.006) < fabs (next_percentage - 0.006))
+ {
+ d->levels->low_input[channel] = i + 1;
+ break;
+ }
+ }
+
+ // Set the high input
+
+ new_count = 0.0;
+
+ for (i = (d->sixteenBit ? 65535 : 255) ; i > 0 ; i--)
+ {
+ new_count += hist->getValue(channel, i);
+ percentage = new_count / count;
+ next_percentage = (new_count + hist->getValue(channel, i - 1)) / count;
+
+ if (fabs (percentage - 0.006) < fabs (next_percentage - 0.006))
+ {
+ d->levels->high_input[channel] = i - 1;
+ break;
+ }
+ }
+ }
+ d->dirty = true;
+}
+
+int ImageLevels::levelsInputFromColor(int channel, const DColor& color)
+{
+ switch (channel)
+ {
+ case ImageHistogram::ValueChannel:
+ return TQMAX (TQMAX (color.red(), color.green()), color.blue());
+
+ case ImageHistogram::RedChannel:
+ return color.red();
+
+ case ImageHistogram::GreenChannel:
+ return color.green();
+
+ case ImageHistogram::BlueChannel:
+ return color.blue();
+ }
+
+ return 0; // just to please the compiler.
+}
+
+void ImageLevels::levelsBlackToneAdjustByColors(int channel, const DColor& color)
+{
+ if (!d->levels) return;
+
+ d->levels->low_input[channel] = levelsInputFromColor(channel, color);
+ d->dirty = true;
+}
+
+void ImageLevels::levelsWhiteToneAdjustByColors(int channel, const DColor& color)
+{
+ if (!d->levels) return;
+
+ d->levels->high_input[channel] = levelsInputFromColor(channel, color);
+ d->dirty = true;
+}
+
+void ImageLevels::levelsGrayToneAdjustByColors(int channel, const DColor& color)
+{
+ if (!d->levels) return;
+
+ int input;
+ int range;
+ double inten;
+ double out_light;
+ unsigned short lightness;
+
+ // Calculate lightness value.
+
+ lightness = (unsigned short)LEVELS_RGB_INTENSITY (color.red(), color.green(), color.blue());
+
+ input = levelsInputFromColor(channel, color);
+
+ range = d->levels->high_input[channel] - d->levels->low_input[channel];
+
+ if (range <= 0)
+ return;
+
+ input -= d->levels->low_input[channel];
+
+ if (input < 0)
+ return;
+
+ // Normalize input and lightness.
+
+ inten = (double) input / (double) range;
+ out_light = (double) lightness/ (double) range;
+
+ if (out_light <= 0)
+ return;
+
+ // Map selected color to corresponding lightness.
+
+ d->levels->gamma[channel] = log (inten) / log (out_light);
+ d->dirty = true;
+}
+
+void ImageLevels::levelsCalculateTransfers()
+{
+ double inten;
+ int i, j;
+
+ if (!d->levels) return;
+
+ // Recalculate the levels arrays.
+
+ for (j = 0 ; j < 5 ; j++)
+ {
+ for (i = 0; i <= (d->sixteenBit ? 65535 : 255); i++)
+ {
+ // determine input intensity.
+
+ if (d->levels->high_input[j] != d->levels->low_input[j])
+ {
+ inten = ((double) (i - d->levels->low_input[j]) /
+ (double) (d->levels->high_input[j] - d->levels->low_input[j]));
+ }
+ else
+ {
+ inten = (double) (i - d->levels->low_input[j]);
+ }
+
+ inten = CLAMP (inten, 0.0, 1.0);
+
+ if (d->levels->gamma[j] != 0.0)
+ inten = pow (inten, (1.0 / d->levels->gamma[j]));
+ }
+ }
+}
+
+float ImageLevels::levelsLutFunc(int n_channels, int channel, float value)
+{
+ double inten;
+ int j;
+
+ if (!d->levels) return 0.0;
+
+ if (n_channels == 1)
+ j = 0;
+ else
+ j = channel + 1;
+
+ inten = value;
+
+ // For color images this runs through the loop with j = channel +1
+ // the first time and j = 0 the second time.
+ //
+ // For bw images this runs through the loop with j = 0 the first and
+ // only time.
+
+ for ( ; j >= 0 ; j -= (channel + 1) )
+ {
+ // Don't apply the overall curve to the alpha channel.
+
+ if (j == 0 && (n_channels == 2 || n_channels == 4)
+ && channel == n_channels -1)
+ return inten;
+
+ // Determine input intensity.
+
+ if (d->levels->high_input[j] != d->levels->low_input[j])
+ inten = ((double) ((float)(d->sixteenBit ? 65535 : 255) * inten - d->levels->low_input[j]) /
+ (double) (d->levels->high_input[j] - d->levels->low_input[j]));
+ else
+ inten = (double) ((float)(d->sixteenBit ? 65535 : 255) * inten - d->levels->low_input[j]);
+
+ if (d->levels->gamma[j] != 0.0)
+ {
+ if (inten >= 0.0)
+ inten = pow ( inten, (1.0 / d->levels->gamma[j]));
+ else
+ inten = -pow (-inten, (1.0 / d->levels->gamma[j]));
+ }
+
+ // determine the output intensity.
+
+ if (d->levels->high_output[j] >= d->levels->low_output[j])
+ inten = (double) (inten * (d->levels->high_output[j] -
+ d->levels->low_output[j]) + d->levels->low_output[j]);
+
+ else if (d->levels->high_output[j] < d->levels->low_output[j])
+ inten = (double) (d->levels->low_output[j] - inten *
+ (d->levels->low_output[j] - d->levels->high_output[j]));
+
+ inten /= (float)(d->sixteenBit ? 65535 : 255);
+ }
+
+ return inten;
+}
+
+void ImageLevels::levelsLutSetup(int nchannels)
+{
+ int i;
+ uint v;
+ double val;
+
+ if (d->lut->luts)
+ {
+ for (i = 0 ; i < d->lut->nchannels ; i++)
+ delete [] d->lut->luts[i];
+
+ delete [] d->lut->luts;
+ }
+
+ d->lut->nchannels = nchannels;
+ d->lut->luts = new unsigned short*[d->lut->nchannels];
+
+ for (i = 0 ; i < d->lut->nchannels ; i++)
+ {
+ d->lut->luts[i] = new unsigned short[(d->sixteenBit ? 65535 : 255) + 1];
+
+ for (v = 0 ; v <= (d->sixteenBit ? 65535 : 255) ; v++)
+ {
+ // to add gamma correction use func(v ^ g) ^ 1/g instead.
+
+ val = (float)(d->sixteenBit ? 65535 : 255) *
+ levelsLutFunc( d->lut->nchannels, i, v/(float)(d->sixteenBit ? 65535 : 255)) + 0.5;
+
+ d->lut->luts[i][v] = (unsigned short)CLAMP (val, 0, (d->sixteenBit ? 65535 : 255));
+ }
+ }
+}
+
+void ImageLevels::levelsLutProcess(uchar *srcPR, uchar *destPR, int w, int h)
+{
+ unsigned short *lut0 = NULL, *lut1 = NULL, *lut2 = NULL, *lut3 = NULL;
+
+ int i;
+
+ if (d->lut->nchannels > 0)
+ lut0 = d->lut->luts[0];
+ if (d->lut->nchannels > 1)
+ lut1 = d->lut->luts[1];
+ if (d->lut->nchannels > 2)
+ lut2 = d->lut->luts[2];
+ if (d->lut->nchannels > 3)
+ lut3 = d->lut->luts[3];
+
+ if (!d->sixteenBit) // 8 bits image.
+ {
+ uchar red, green, blue, alpha;
+ uchar *ptr = srcPR;
+ uchar *dst = destPR;
+
+ for (i = 0 ; i < w*h ; i++)
+ {
+ blue = ptr[0];
+ green = ptr[1];
+ red = ptr[2];
+ alpha = ptr[3];
+
+ if ( d->lut->nchannels > 0 )
+ red = lut0[red];
+
+ if ( d->lut->nchannels > 1 )
+ green = lut1[green];
+
+ if ( d->lut->nchannels > 2 )
+ blue = lut2[blue];
+
+ if ( d->lut->nchannels > 3 )
+ alpha = lut3[alpha];
+
+ dst[0] = blue;
+ dst[1] = green;
+ dst[2] = red;
+ dst[3] = alpha;
+
+ ptr += 4;
+ dst += 4;
+ }
+ }
+ else // 16 bits image.
+ {
+ unsigned short red, green, blue, alpha;
+ unsigned short *ptr = (unsigned short *)srcPR;
+ unsigned short *dst = (unsigned short *)destPR;
+
+ for (i = 0 ; i < w*h ; i++)
+ {
+ blue = ptr[0];
+ green = ptr[1];
+ red = ptr[2];
+ alpha = ptr[3];
+
+ if ( d->lut->nchannels > 0 )
+ red = lut0[red];
+
+ if ( d->lut->nchannels > 1 )
+ green = lut1[green];
+
+ if ( d->lut->nchannels > 2 )
+ blue = lut2[blue];
+
+ if ( d->lut->nchannels > 3 )
+ alpha = lut3[alpha];
+
+ dst[0] = blue;
+ dst[1] = green;
+ dst[2] = red;
+ dst[3] = alpha;
+
+ ptr += 4;
+ dst += 4;
+ }
+ }
+}
+
+void ImageLevels::setLevelGammaValue(int Channel, double val)
+{
+ if ( d->levels && Channel>=0 && Channel<5 )
+ {
+ d->levels->gamma[Channel] = val;
+ d->dirty = true;
+ }
+}
+
+void ImageLevels::setLevelLowInputValue(int Channel, int val)
+{
+ if ( d->levels && Channel>=0 && Channel<5 )
+ {
+ d->levels->low_input[Channel] = val;
+ d->dirty = true;
+ }
+}
+
+void ImageLevels::setLevelHighInputValue(int Channel, int val)
+{
+ if ( d->levels && Channel>=0 && Channel<5 )
+ {
+ d->levels->high_input[Channel] = val;
+ d->dirty = true;
+ }
+}
+
+void ImageLevels::setLevelLowOutputValue(int Channel, int val)
+{
+ if ( d->levels && Channel>=0 && Channel<5 )
+ {
+ d->levels->low_output[Channel] = val;
+ d->dirty = true;
+ }
+}
+
+void ImageLevels::setLevelHighOutputValue(int Channel, int val)
+{
+ if ( d->levels && Channel>=0 && Channel<5 )
+ {
+ d->levels->high_output[Channel] = val;
+ d->dirty = true;
+ }
+}
+
+double ImageLevels::getLevelGammaValue(int Channel)
+{
+ if ( d->levels && Channel>=0 && Channel<5 )
+ return (d->levels->gamma[Channel]);
+
+ return 0.0;
+}
+
+int ImageLevels::getLevelLowInputValue(int Channel)
+{
+ if ( d->levels && Channel>=0 && Channel<5 )
+ return (d->levels->low_input[Channel]);
+
+ return 0;
+}
+
+int ImageLevels::getLevelHighInputValue(int Channel)
+{
+ if ( d->levels && Channel>=0 && Channel<5 )
+ return (d->levels->high_input[Channel]);
+
+ return 0;
+}
+
+int ImageLevels::getLevelLowOutputValue(int Channel)
+{
+ if ( d->levels && Channel>=0 && Channel<5 )
+ return (d->levels->low_output[Channel]);
+
+ return 0;
+}
+
+int ImageLevels::getLevelHighOutputValue(int Channel)
+{
+ if ( d->levels && Channel>=0 && Channel<5 )
+ return (d->levels->high_output[Channel]);
+
+ return 0;
+}
+
+bool ImageLevels::loadLevelsFromGimpLevelsFile(const KURL& fileUrl)
+{
+ // TODO : support KURL !
+
+ FILE *file;
+ int low_input[5];
+ int high_input[5];
+ int low_output[5];
+ int high_output[5];
+ double gamma[5];
+ int i, fields;
+ char buf[50];
+ char *nptr;
+
+ file = fopen(TQFile::encodeName(fileUrl.path()), "r");
+
+ if (!file)
+ return false;
+
+ if (! fgets (buf, sizeof (buf), file))
+ {
+ fclose(file);
+ return false;
+ }
+
+ if (strcmp (buf, "# GIMP Levels File\n") != 0)
+ {
+ fclose(file);
+ return false;
+ }
+
+ for (i = 0 ; i < 5 ; i++)
+ {
+ fields = fscanf (file, "%d %d %d %d ",
+ &low_input[i],
+ &high_input[i],
+ &low_output[i],
+ &high_output[i]);
+
+ if (fields != 4)
+ {
+ DWarning() << "Invalid Gimp levels file!" << endl;
+ fclose(file);
+ return false;
+ }
+
+ if (!fgets (buf, 50, file))
+ {
+ DWarning() << "Invalid Gimp levels file!" << endl;
+ fclose(file);
+ return false;
+ }
+
+ gamma[i] = strtod (buf, &nptr);
+
+ if (buf == nptr || errno == ERANGE)
+ {
+ DWarning() << "Invalid Gimp levels file!" << endl;
+ fclose(file);
+ return false;
+ }
+ }
+
+ for (i = 0 ; i < 5 ; i++)
+ {
+ setLevelGammaValue(i, gamma[i]);
+ setLevelLowInputValue(i, d->sixteenBit ? low_input[i]*255 : low_input[i]);
+ setLevelHighInputValue(i, d->sixteenBit ? high_input[i]*255 : high_input[i]);
+ setLevelLowOutputValue(i, d->sixteenBit ? low_output[i]*255 : low_output[i]);
+ setLevelHighOutputValue(i, d->sixteenBit ? high_output[i]*255 : high_output[i]);
+ }
+
+ fclose(file);
+ return true;
+}
+
+bool ImageLevels::saveLevelsToGimpLevelsFile(const KURL& fileUrl)
+{
+ // TODO : support KURL !
+
+ FILE *file;
+ int i;
+
+ file = fopen(TQFile::encodeName(fileUrl.path()), "w");
+
+ if (!file)
+ return false;
+
+ fprintf (file, "# GIMP Levels File\n");
+
+ for (i = 0 ; i < 5 ; i++)
+ {
+ char buf[256];
+ sprintf (buf, "%f", getLevelGammaValue(i));
+
+ fprintf (file, "%d %d %d %d %s\n",
+ d->sixteenBit ? getLevelLowInputValue(i)/255 : getLevelLowInputValue(i),
+ d->sixteenBit ? getLevelHighInputValue(i)/255 : getLevelHighInputValue(i),
+ d->sixteenBit ? getLevelLowOutputValue(i)/255 : getLevelLowOutputValue(i),
+ d->sixteenBit ? getLevelHighInputValue(i)/255 : getLevelHighInputValue(i),
+ buf);
+ }
+
+ fflush(file);
+ fclose(file);
+
+ return true;
+}
+
+} // NameSpace Digikam