|
|
|
/* ============================================================
|
|
|
|
*
|
|
|
|
* This file is a part of digiKam project
|
|
|
|
* http://www.digikam.org
|
|
|
|
*
|
|
|
|
* Date : 2004-12-01
|
|
|
|
* Description : a widget to draw histogram curves
|
|
|
|
*
|
|
|
|
* Copyright (C) 2004-2008 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* ============================================================ */
|
|
|
|
|
|
|
|
#define CLAMP(x,l,u) ((x)<(l)?(l):((x)>(u)?(u):(x)))
|
|
|
|
|
|
|
|
// C++ includes.
|
|
|
|
|
|
|
|
#include <cmath>
|
|
|
|
#include <cstdlib>
|
|
|
|
|
|
|
|
// TQt includes.
|
|
|
|
|
|
|
|
#include <tqpixmap.h>
|
|
|
|
#include <tqpainter.h>
|
|
|
|
#include <tqpoint.h>
|
|
|
|
#include <tqpen.h>
|
|
|
|
#include <tqevent.h>
|
|
|
|
#include <tqtimer.h>
|
|
|
|
#include <tqrect.h>
|
|
|
|
#include <tqcolor.h>
|
|
|
|
#include <tqfont.h>
|
|
|
|
#include <tqfontmetrics.h>
|
|
|
|
|
|
|
|
// KDE includes.
|
|
|
|
|
|
|
|
#include <kcursor.h>
|
|
|
|
#include <tdelocale.h>
|
|
|
|
|
|
|
|
// Digikam includes.
|
|
|
|
|
|
|
|
#include "ddebug.h"
|
|
|
|
#include "imagehistogram.h"
|
|
|
|
#include "imagecurves.h"
|
|
|
|
|
|
|
|
// Local includes.
|
|
|
|
|
|
|
|
#include "curveswidget.h"
|
|
|
|
#include "curveswidget.moc"
|
|
|
|
|
|
|
|
namespace Digikam
|
|
|
|
{
|
|
|
|
|
|
|
|
class CurvesWidgetPriv
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
enum RepaintType
|
|
|
|
{
|
|
|
|
HistogramDataLoading = 0, // Image Data loading in progress.
|
|
|
|
HistogramNone, // No current histogram values calculation.
|
|
|
|
HistogramStarted, // Histogram values calculation started.
|
|
|
|
HistogramCompleted, // Histogram values calculation completed.
|
|
|
|
HistogramFailed // Histogram values calculation failed.
|
|
|
|
};
|
|
|
|
|
|
|
|
CurvesWidgetPriv()
|
|
|
|
{
|
|
|
|
blinkTimer = 0;
|
|
|
|
curves = 0;
|
|
|
|
grabPoint = -1;
|
|
|
|
last = 0;
|
|
|
|
guideVisible = false;
|
|
|
|
xMouseOver = -1;
|
|
|
|
yMouseOver = -1;
|
|
|
|
clearFlag = HistogramNone;
|
|
|
|
pos = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int clearFlag; // Clear drawing zone with message.
|
|
|
|
int leftMost;
|
|
|
|
int rightMost;
|
|
|
|
int grabPoint;
|
|
|
|
int last;
|
|
|
|
int xMouseOver;
|
|
|
|
int yMouseOver;
|
|
|
|
int pos; // Position of animation during loading/calculation.
|
|
|
|
|
|
|
|
bool sixteenBits;
|
|
|
|
bool readOnlyMode;
|
|
|
|
bool guideVisible;
|
|
|
|
|
|
|
|
DColor colorGuide;
|
|
|
|
|
|
|
|
TQTimer *blinkTimer;
|
|
|
|
|
|
|
|
ImageCurves *curves; // Curves data instance.
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
CurvesWidget::CurvesWidget(int w, int h, TQWidget *parent, bool readOnly)
|
|
|
|
: TQWidget(parent, 0, TQt::WDestructiveClose)
|
|
|
|
{
|
|
|
|
d = new CurvesWidgetPriv;
|
|
|
|
|
|
|
|
setup(w, h, readOnly);
|
|
|
|
}
|
|
|
|
|
|
|
|
CurvesWidget::CurvesWidget(int w, int h,
|
|
|
|
uchar *i_data, uint i_w, uint i_h, bool i_sixteenBits,
|
|
|
|
TQWidget *parent, bool readOnly)
|
|
|
|
: TQWidget(parent, 0, TQt::WDestructiveClose)
|
|
|
|
{
|
|
|
|
d = new CurvesWidgetPriv;
|
|
|
|
|
|
|
|
setup(w, h, readOnly);
|
|
|
|
updateData(i_data, i_w, i_h, i_sixteenBits);
|
|
|
|
}
|
|
|
|
|
|
|
|
CurvesWidget::~CurvesWidget()
|
|
|
|
{
|
|
|
|
d->blinkTimer->stop();
|
|
|
|
|
|
|
|
if (m_imageHistogram)
|
|
|
|
delete m_imageHistogram;
|
|
|
|
|
|
|
|
if (d->curves)
|
|
|
|
delete d->curves;
|
|
|
|
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesWidget::setup(int w, int h, bool readOnly)
|
|
|
|
{
|
|
|
|
d->readOnlyMode = readOnly;
|
|
|
|
d->curves = new ImageCurves(true);
|
|
|
|
m_channelType = ValueHistogram;
|
|
|
|
m_scaleType = LogScaleHistogram;
|
|
|
|
m_imageHistogram = 0;
|
|
|
|
|
|
|
|
setMouseTracking(true);
|
|
|
|
setPaletteBackgroundColor(colorGroup().background());
|
|
|
|
setMinimumSize(w, h);
|
|
|
|
|
|
|
|
d->blinkTimer = new TQTimer( this );
|
|
|
|
|
|
|
|
connect(d->blinkTimer, TQT_SIGNAL(timeout()),
|
|
|
|
this, TQT_SLOT(slotBlinkTimerDone()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesWidget::updateData(uchar *i_data, uint i_w, uint i_h, bool i_sixteenBits)
|
|
|
|
{
|
|
|
|
stopHistogramComputation();
|
|
|
|
|
|
|
|
d->sixteenBits = i_sixteenBits;
|
|
|
|
|
|
|
|
// Remove old histogram data from memory.
|
|
|
|
if (m_imageHistogram)
|
|
|
|
delete m_imageHistogram;
|
|
|
|
|
|
|
|
// Calc new histogram data
|
|
|
|
m_imageHistogram = new ImageHistogram(i_data, i_w, i_h, i_sixteenBits, TQT_TQOBJECT(this));
|
|
|
|
|
|
|
|
if (d->curves)
|
|
|
|
delete d->curves;
|
|
|
|
|
|
|
|
d->curves = new ImageCurves(i_sixteenBits);
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesWidget::reset()
|
|
|
|
{
|
|
|
|
if (d->curves)
|
|
|
|
d->curves->curvesReset();
|
|
|
|
|
|
|
|
d->grabPoint = -1;
|
|
|
|
d->guideVisible = false;
|
|
|
|
repaint(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
ImageCurves* CurvesWidget::curves() const
|
|
|
|
{
|
|
|
|
return d->curves;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesWidget::setDataLoading()
|
|
|
|
{
|
|
|
|
if (d->clearFlag != CurvesWidgetPriv::HistogramDataLoading)
|
|
|
|
{
|
|
|
|
setCursor(KCursor::waitCursor());
|
|
|
|
d->clearFlag = CurvesWidgetPriv::HistogramDataLoading;
|
|
|
|
d->pos = 0;
|
|
|
|
d->blinkTimer->start(100);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesWidget::setLoadingFailed()
|
|
|
|
{
|
|
|
|
d->clearFlag = CurvesWidgetPriv::HistogramFailed;
|
|
|
|
d->pos = 0;
|
|
|
|
d->blinkTimer->stop();
|
|
|
|
repaint(false);
|
|
|
|
setCursor(KCursor::arrowCursor());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesWidget::setCurveGuide(const DColor& color)
|
|
|
|
{
|
|
|
|
d->guideVisible = true;
|
|
|
|
d->colorGuide = color;
|
|
|
|
repaint(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesWidget::curveTypeChanged()
|
|
|
|
{
|
|
|
|
switch (d->curves->getCurveType(m_channelType))
|
|
|
|
{
|
|
|
|
case ImageCurves::CURVE_SMOOTH:
|
|
|
|
|
|
|
|
// pick representative points from the curve and make them control points
|
|
|
|
|
|
|
|
for (int i = 0; i <= 8; i++)
|
|
|
|
{
|
|
|
|
int index = CLAMP(i * m_imageHistogram->getHistogramSegment()/8,
|
|
|
|
0, m_imageHistogram->getHistogramSegment()-1);
|
|
|
|
|
|
|
|
d->curves->setCurvePoint( m_channelType,
|
|
|
|
i * 2, TQPoint(index,
|
|
|
|
d->curves->getCurveValue(m_channelType,
|
|
|
|
index)) );
|
|
|
|
}
|
|
|
|
|
|
|
|
d->curves->curvesCalculateCurve(m_channelType);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ImageCurves::CURVE_FREE:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
repaint(false);
|
|
|
|
emit signalCurvesChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesWidget::customEvent(TQCustomEvent *event)
|
|
|
|
{
|
|
|
|
if (!event) return;
|
|
|
|
|
|
|
|
ImageHistogram::EventData *ed = (ImageHistogram::EventData*) event->data();
|
|
|
|
|
|
|
|
if (!ed) return;
|
|
|
|
|
|
|
|
if (ed->starting)
|
|
|
|
{
|
|
|
|
setCursor(KCursor::waitCursor());
|
|
|
|
d->clearFlag = CurvesWidgetPriv::HistogramStarted;
|
|
|
|
d->blinkTimer->start(200);
|
|
|
|
repaint(false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (ed->success)
|
|
|
|
{
|
|
|
|
// Repaint histogram
|
|
|
|
d->clearFlag = CurvesWidgetPriv::HistogramCompleted;
|
|
|
|
d->blinkTimer->stop();
|
|
|
|
repaint(false);
|
|
|
|
setCursor(KCursor::arrowCursor());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
d->clearFlag = CurvesWidgetPriv::HistogramFailed;
|
|
|
|
d->blinkTimer->stop();
|
|
|
|
repaint(false);
|
|
|
|
setCursor(KCursor::arrowCursor());
|
|
|
|
emit signalHistogramComputationFailed();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
delete ed;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesWidget::stopHistogramComputation()
|
|
|
|
{
|
|
|
|
if (m_imageHistogram)
|
|
|
|
m_imageHistogram->stopCalcHistogramValues();
|
|
|
|
|
|
|
|
d->blinkTimer->stop();
|
|
|
|
d->pos = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesWidget::slotBlinkTimerDone()
|
|
|
|
{
|
|
|
|
repaint(false);
|
|
|
|
d->blinkTimer->start(200);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesWidget::paintEvent(TQPaintEvent*)
|
|
|
|
{
|
|
|
|
if (d->clearFlag == CurvesWidgetPriv::HistogramDataLoading ||
|
|
|
|
d->clearFlag == CurvesWidgetPriv::HistogramStarted)
|
|
|
|
{
|
|
|
|
// In first, we draw an animation.
|
|
|
|
|
|
|
|
int asize = 24;
|
|
|
|
TQPixmap anim(asize, asize);
|
|
|
|
TQPainter p2;
|
|
|
|
p2.begin(TQT_TQPAINTDEVICE(&anim), this);
|
|
|
|
p2.fillRect(0, 0, asize, asize, palette().active().background());
|
|
|
|
p2.translate(asize/2, asize/2);
|
|
|
|
|
|
|
|
d->pos = (d->pos + 10) % 360;
|
|
|
|
p2.setPen(TQPen(palette().active().text()));
|
|
|
|
p2.rotate(d->pos);
|
|
|
|
for ( int i=0 ; i<12 ; i++ )
|
|
|
|
{
|
|
|
|
p2.drawLine(asize/2-5, 0, asize/2-2, 0);
|
|
|
|
p2.rotate(30);
|
|
|
|
}
|
|
|
|
p2.end();
|
|
|
|
|
|
|
|
// ... and we render busy text.
|
|
|
|
|
|
|
|
TQPixmap pm(size());
|
|
|
|
TQPainter p1;
|
|
|
|
p1.begin(TQT_TQPAINTDEVICE(&pm), this);
|
|
|
|
p1.fillRect(0, 0, width(), height(), palette().active().background());
|
|
|
|
p1.setPen(TQPen(palette().active().foreground(), 1, TQt::SolidLine));
|
|
|
|
p1.drawRect(0, 0, width(), height());
|
|
|
|
p1.drawPixmap(width()/2 - asize /2, asize, anim);
|
|
|
|
p1.setPen(TQPen(palette().active().text()));
|
|
|
|
|
|
|
|
if (d->clearFlag == CurvesWidgetPriv::HistogramDataLoading)
|
|
|
|
p1.drawText(0, 0, width(), height(), TQt::AlignCenter,
|
|
|
|
i18n("Loading image..."));
|
|
|
|
else
|
|
|
|
p1.drawText(0, 0, width(), height(), TQt::AlignCenter,
|
|
|
|
i18n("Histogram calculation..."));
|
|
|
|
|
|
|
|
p1.end();
|
|
|
|
bitBlt(this, 0, 0, &pm);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (d->clearFlag == CurvesWidgetPriv::HistogramFailed)
|
|
|
|
{
|
|
|
|
TQPixmap pm(size());
|
|
|
|
TQPainter p1;
|
|
|
|
p1.begin(TQT_TQPAINTDEVICE(&pm), this);
|
|
|
|
p1.fillRect(0, 0, width(), height(), palette().active().background());
|
|
|
|
p1.setPen(TQPen(palette().active().foreground(), 1, TQt::SolidLine));
|
|
|
|
p1.drawRect(0, 0, width(), height());
|
|
|
|
p1.setPen(TQPen(palette().active().text()));
|
|
|
|
p1.drawText(0, 0, width(), height(), TQt::AlignCenter,
|
|
|
|
i18n("Histogram\ncalculation\nfailed."));
|
|
|
|
p1.end();
|
|
|
|
bitBlt(this, 0, 0, &pm);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_imageHistogram) return;
|
|
|
|
|
|
|
|
int x, y;
|
|
|
|
int wWidth = width();
|
|
|
|
int wHeight = height();
|
|
|
|
double max;
|
|
|
|
class ImageHistogram *histogram = m_imageHistogram;
|
|
|
|
|
|
|
|
x = 0;
|
|
|
|
y = 0;
|
|
|
|
max = 0.0;
|
|
|
|
|
|
|
|
switch(m_channelType)
|
|
|
|
{
|
|
|
|
case CurvesWidget::GreenChannelHistogram: // Green channel.
|
|
|
|
max = histogram->getMaximum(ImageHistogram::GreenChannel);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CurvesWidget::BlueChannelHistogram: // Blue channel.
|
|
|
|
max = histogram->getMaximum(ImageHistogram::BlueChannel);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CurvesWidget::RedChannelHistogram: // Red channel.
|
|
|
|
max = histogram->getMaximum(ImageHistogram::RedChannel);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CurvesWidget::AlphaChannelHistogram: // Alpha channel.
|
|
|
|
max = histogram->getMaximum(ImageHistogram::AlphaChannel);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CurvesWidget::ValueHistogram: // Luminosity.
|
|
|
|
max = histogram->getMaximum(ImageHistogram::ValueChannel);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (m_scaleType)
|
|
|
|
{
|
|
|
|
case CurvesWidget::LinScaleHistogram:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CurvesWidget::LogScaleHistogram:
|
|
|
|
if (max > 0.0)
|
|
|
|
max = log (max);
|
|
|
|
else
|
|
|
|
max = 1.0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Drawing selection or all histogram values.
|
|
|
|
// A TQPixmap is used for enable the double buffering.
|
|
|
|
|
|
|
|
TQPixmap pm(size());
|
|
|
|
TQPainter p1;
|
|
|
|
p1.begin(TQT_TQPAINTDEVICE(&pm), this);
|
|
|
|
|
|
|
|
int curvePrevVal = 0;
|
|
|
|
|
|
|
|
for (x = 0 ; x < wWidth ; x++)
|
|
|
|
{
|
|
|
|
double value = 0.0;
|
|
|
|
int i, j;
|
|
|
|
int curveVal;
|
|
|
|
|
|
|
|
i = (x * histogram->getHistogramSegment()) / wWidth;
|
|
|
|
j = ((x + 1) * histogram->getHistogramSegment()) / wWidth;
|
|
|
|
|
|
|
|
curveVal = d->curves->getCurveValue(m_channelType, i);
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
double v = 0.0;
|
|
|
|
|
|
|
|
switch(m_channelType)
|
|
|
|
{
|
|
|
|
case CurvesWidget::RedChannelHistogram: // Red channel.
|
|
|
|
v = histogram->getValue(ImageHistogram::RedChannel, i++);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CurvesWidget::GreenChannelHistogram: // Green channel.
|
|
|
|
v = histogram->getValue(ImageHistogram::GreenChannel, i++);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CurvesWidget::BlueChannelHistogram: // Blue channel.
|
|
|
|
v = histogram->getValue(ImageHistogram::BlueChannel, i++);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CurvesWidget::AlphaChannelHistogram: // Alpha channel.
|
|
|
|
v = histogram->getValue(ImageHistogram::AlphaChannel, i++);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CurvesWidget::ValueHistogram: // Luminosity.
|
|
|
|
v = histogram->getValue(ImageHistogram::ValueChannel, i++);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (v > value)
|
|
|
|
value = v;
|
|
|
|
}
|
|
|
|
while (i < j);
|
|
|
|
|
|
|
|
switch (m_scaleType)
|
|
|
|
{
|
|
|
|
case CurvesWidget::LinScaleHistogram:
|
|
|
|
y = (int) ((wHeight * value) / max);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CurvesWidget::LogScaleHistogram:
|
|
|
|
if (value <= 0.0) value = 1.0;
|
|
|
|
y = (int) ((wHeight * log (value)) / max);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
y = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Drawing histogram
|
|
|
|
|
|
|
|
p1.setPen(TQPen(palette().active().foreground(), 1, TQt::SolidLine));
|
|
|
|
p1.drawLine(x, wHeight, x, wHeight - y);
|
|
|
|
p1.setPen(TQPen(palette().active().background(), 1, TQt::SolidLine));
|
|
|
|
p1.drawLine(x, wHeight - y, x, 0);
|
|
|
|
|
|
|
|
// Drawing curves.
|
|
|
|
|
|
|
|
p1.setPen(TQPen(palette().active().link(), 2, TQt::SolidLine));
|
|
|
|
p1.drawLine(x - 1, wHeight - ((curvePrevVal * wHeight) / histogram->getHistogramSegment()),
|
|
|
|
x, wHeight - ((curveVal * wHeight) / histogram->getHistogramSegment()));
|
|
|
|
|
|
|
|
curvePrevVal = curveVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Drawing curves points.
|
|
|
|
|
|
|
|
if (!d->readOnlyMode && d->curves->getCurveType(m_channelType) == ImageCurves::CURVE_SMOOTH)
|
|
|
|
{
|
|
|
|
p1.setPen(TQPen(TQt::red, 3, TQt::SolidLine));
|
|
|
|
|
|
|
|
for (int p = 0 ; p < 17 ; p++)
|
|
|
|
{
|
|
|
|
TQPoint curvePoint = d->curves->getCurvePoint(m_channelType, p);
|
|
|
|
|
|
|
|
if (curvePoint.x() >= 0)
|
|
|
|
{
|
|
|
|
p1.drawEllipse( ((curvePoint.x() * wWidth) / histogram->getHistogramSegment()) - 2,
|
|
|
|
wHeight - 2 - ((curvePoint.y() * wHeight) / histogram->getHistogramSegment()),
|
|
|
|
4, 4 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Drawing black/middle/highlight tone grid separators.
|
|
|
|
|
|
|
|
p1.setPen(TQPen(palette().active().base(), 1, TQt::SolidLine));
|
|
|
|
p1.drawLine(wWidth/4, 0, wWidth/4, wHeight);
|
|
|
|
p1.drawLine(wWidth/2, 0, wWidth/2, wHeight);
|
|
|
|
p1.drawLine(3*wWidth/4, 0, 3*wWidth/4, wHeight);
|
|
|
|
p1.drawLine(0, wHeight/4, wWidth, wHeight/4);
|
|
|
|
p1.drawLine(0, wHeight/2, wWidth, wHeight/2);
|
|
|
|
p1.drawLine(0, 3*wHeight/4, wWidth, 3*wHeight/4);
|
|
|
|
|
|
|
|
// Drawing X,Y point position dragged by mouse over widget.
|
|
|
|
|
|
|
|
p1.setPen(TQPen(TQt::red, 1, TQt::DotLine));
|
|
|
|
|
|
|
|
if (d->xMouseOver != -1 && d->yMouseOver != -1)
|
|
|
|
{
|
|
|
|
TQString string = i18n("x:%1\ny:%2").arg(d->xMouseOver).arg(d->yMouseOver);
|
|
|
|
TQFontMetrics fontMt(string);
|
|
|
|
TQRect rect = fontMt.boundingRect(0, 0, wWidth, wHeight, 0, string);
|
|
|
|
rect.moveRight(wWidth);
|
|
|
|
rect.moveBottom(wHeight);
|
|
|
|
p1.drawText(rect, TQt::AlignLeft||TQt::AlignTop, string);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Drawing color guide.
|
|
|
|
|
|
|
|
int guidePos;
|
|
|
|
|
|
|
|
if (d->guideVisible)
|
|
|
|
{
|
|
|
|
switch(m_channelType)
|
|
|
|
{
|
|
|
|
case CurvesWidget::RedChannelHistogram:
|
|
|
|
guidePos = d->colorGuide.red();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CurvesWidget::GreenChannelHistogram:
|
|
|
|
guidePos = d->colorGuide.green();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CurvesWidget::BlueChannelHistogram:
|
|
|
|
guidePos = d->colorGuide.blue();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CurvesWidget::ValueHistogram:
|
|
|
|
guidePos = TQMAX(TQMAX(d->colorGuide.red(), d->colorGuide.green()), d->colorGuide.blue());
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: // Alpha.
|
|
|
|
guidePos = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (guidePos != -1)
|
|
|
|
{
|
|
|
|
int xGuide = (guidePos * wWidth) / histogram->getHistogramSegment();
|
|
|
|
p1.drawLine(xGuide, 0, xGuide, wHeight);
|
|
|
|
|
|
|
|
TQString string = i18n("x:%1").arg(guidePos);
|
|
|
|
TQFontMetrics fontMt( string );
|
|
|
|
TQRect rect = fontMt.boundingRect(0, 0, wWidth, wHeight, 0, string);
|
|
|
|
p1.setPen(TQPen(TQt::red, 1, TQt::SolidLine));
|
|
|
|
rect.moveTop(1);
|
|
|
|
|
|
|
|
if (xGuide < wWidth/2)
|
|
|
|
{
|
|
|
|
rect.moveLeft(xGuide);
|
|
|
|
p1.fillRect(rect, TQBrush(TQColor(250, 250, 255)));
|
|
|
|
p1.drawRect(rect);
|
|
|
|
rect.moveLeft(xGuide+3);
|
|
|
|
p1.drawText(rect, TQt::AlignLeft, string);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rect.moveRight(xGuide);
|
|
|
|
p1.fillRect(rect, TQBrush(TQColor(250, 250, 255)));
|
|
|
|
p1.drawRect(rect);
|
|
|
|
rect.moveRight(xGuide-3);
|
|
|
|
p1.drawText(rect, TQt::AlignRight, string);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Drawing frame.
|
|
|
|
|
|
|
|
p1.setPen(TQPen(palette().active().foreground(), 1, TQt::SolidLine));
|
|
|
|
p1.drawRect(0, 0, width(), height());
|
|
|
|
|
|
|
|
p1.end();
|
|
|
|
bitBlt(this, 0, 0, &pm);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesWidget::mousePressEvent(TQMouseEvent *e)
|
|
|
|
{
|
|
|
|
if (d->readOnlyMode || !m_imageHistogram) return;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
int closest_point;
|
|
|
|
int distance;
|
|
|
|
|
|
|
|
if (e->button() != Qt::LeftButton || d->clearFlag == CurvesWidgetPriv::HistogramStarted)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int x = CLAMP((int)(e->pos().x() *
|
|
|
|
((float)(m_imageHistogram->getHistogramSegment()-1) / (float)width())),
|
|
|
|
0, m_imageHistogram->getHistogramSegment()-1 );
|
|
|
|
int y = CLAMP((int)(e->pos().y() *
|
|
|
|
((float)(m_imageHistogram->getHistogramSegment()-1) / (float)height())),
|
|
|
|
0, m_imageHistogram->getHistogramSegment()-1 );
|
|
|
|
|
|
|
|
distance = 65536;
|
|
|
|
|
|
|
|
for (i = 0, closest_point = 0 ; i < 17 ; i++)
|
|
|
|
{
|
|
|
|
int xcurvepoint = d->curves->getCurvePointX(m_channelType, i);
|
|
|
|
|
|
|
|
if (xcurvepoint != -1)
|
|
|
|
{
|
|
|
|
if (abs (x - xcurvepoint) < distance)
|
|
|
|
{
|
|
|
|
distance = abs (x - xcurvepoint);
|
|
|
|
closest_point = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int delta = m_imageHistogram->getHistogramSegment()/16;
|
|
|
|
if (distance > 8)
|
|
|
|
closest_point = (x + delta/2) / delta;
|
|
|
|
|
|
|
|
setCursor(KCursor::crossCursor());
|
|
|
|
|
|
|
|
switch(d->curves->getCurveType(m_channelType))
|
|
|
|
{
|
|
|
|
case ImageCurves::CURVE_SMOOTH:
|
|
|
|
{
|
|
|
|
// Determine the leftmost and rightmost points.
|
|
|
|
|
|
|
|
d->leftMost = -1;
|
|
|
|
|
|
|
|
for (i = closest_point - 1 ; i >= 0 ; i--)
|
|
|
|
{
|
|
|
|
if (d->curves->getCurvePointX(m_channelType, i) != -1)
|
|
|
|
{
|
|
|
|
d->leftMost = d->curves->getCurvePointX(m_channelType, i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
d->rightMost = m_imageHistogram->getHistogramSegment();
|
|
|
|
|
|
|
|
for (i = closest_point + 1 ; i < 17 ; i++)
|
|
|
|
{
|
|
|
|
if (d->curves->getCurvePointX(m_channelType, i) != -1)
|
|
|
|
{
|
|
|
|
d->rightMost = d->curves->getCurvePointX(m_channelType, i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
d->grabPoint = closest_point;
|
|
|
|
d->curves->setCurvePoint(m_channelType, d->grabPoint,
|
|
|
|
TQPoint(x, m_imageHistogram->getHistogramSegment() - y));
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ImageCurves::CURVE_FREE:
|
|
|
|
{
|
|
|
|
|
|
|
|
d->curves->setCurveValue(m_channelType, x, m_imageHistogram->getHistogramSegment() - y);
|
|
|
|
d->grabPoint = x;
|
|
|
|
d->last = y;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
d->curves->curvesCalculateCurve(m_channelType);
|
|
|
|
repaint(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesWidget::mouseReleaseEvent(TQMouseEvent *e)
|
|
|
|
{
|
|
|
|
if (d->readOnlyMode || !m_imageHistogram) return;
|
|
|
|
|
|
|
|
if (e->button() != Qt::LeftButton || d->clearFlag == CurvesWidgetPriv::HistogramStarted)
|
|
|
|
return;
|
|
|
|
|
|
|
|
setCursor(KCursor::arrowCursor());
|
|
|
|
d->grabPoint = -1;
|
|
|
|
d->curves->curvesCalculateCurve(m_channelType);
|
|
|
|
repaint(false);
|
|
|
|
emit signalCurvesChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesWidget::mouseMoveEvent(TQMouseEvent *e)
|
|
|
|
{
|
|
|
|
if (d->readOnlyMode || !m_imageHistogram) return;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
int closest_point;
|
|
|
|
int x1, x2, y1, y2;
|
|
|
|
int distance;
|
|
|
|
|
|
|
|
if (d->clearFlag == CurvesWidgetPriv::HistogramStarted)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int x = CLAMP( (int)(e->pos().x()*((float)(m_imageHistogram->getHistogramSegment()-1)/(float)width())),
|
|
|
|
0, m_imageHistogram->getHistogramSegment()-1 );
|
|
|
|
int y = CLAMP( (int)(e->pos().y()*((float)(m_imageHistogram->getHistogramSegment()-1)/(float)height())),
|
|
|
|
0, m_imageHistogram->getHistogramSegment()-1 );
|
|
|
|
|
|
|
|
distance = 65536;
|
|
|
|
|
|
|
|
for (i = 0, closest_point = 0 ; i < 17 ; i++)
|
|
|
|
{
|
|
|
|
if (d->curves->getCurvePointX(m_channelType, i) != -1)
|
|
|
|
{
|
|
|
|
if (abs (x - d->curves->getCurvePointX(m_channelType, i)) < distance)
|
|
|
|
{
|
|
|
|
distance = abs (x - d->curves->getCurvePointX(m_channelType, i));
|
|
|
|
closest_point = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int delta = m_imageHistogram->getHistogramSegment()/16;
|
|
|
|
if (distance > 8)
|
|
|
|
closest_point = (x + delta/2) / delta;
|
|
|
|
|
|
|
|
switch ( d->curves->getCurveType(m_channelType) )
|
|
|
|
{
|
|
|
|
case ImageCurves::CURVE_SMOOTH:
|
|
|
|
{
|
|
|
|
if (d->grabPoint == -1) // If no point is grabbed...
|
|
|
|
{
|
|
|
|
if ( d->curves->getCurvePointX(m_channelType, closest_point) != -1 )
|
|
|
|
setCursor(KCursor::arrowCursor());
|
|
|
|
else
|
|
|
|
setCursor(KCursor::crossCursor());
|
|
|
|
}
|
|
|
|
else // Else, drag the grabbed point
|
|
|
|
{
|
|
|
|
setCursor(KCursor::crossCursor());
|
|
|
|
|
|
|
|
d->curves->setCurvePointX(m_channelType, d->grabPoint, -1);
|
|
|
|
|
|
|
|
if (x > d->leftMost && x < d->rightMost)
|
|
|
|
{
|
|
|
|
closest_point = (x + delta/2) / delta;
|
|
|
|
|
|
|
|
if (d->curves->getCurvePointX(m_channelType, closest_point) == -1)
|
|
|
|
d->grabPoint = closest_point;
|
|
|
|
|
|
|
|
d->curves->setCurvePoint(m_channelType, d->grabPoint,
|
|
|
|
TQPoint(x, m_imageHistogram->getHistogramSegment()-1 - y));
|
|
|
|
}
|
|
|
|
|
|
|
|
d->curves->curvesCalculateCurve(m_channelType);
|
|
|
|
emit signalCurvesChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ImageCurves::CURVE_FREE:
|
|
|
|
{
|
|
|
|
if (d->grabPoint != -1)
|
|
|
|
{
|
|
|
|
if (d->grabPoint > x)
|
|
|
|
{
|
|
|
|
x1 = x;
|
|
|
|
x2 = d->grabPoint;
|
|
|
|
y1 = y;
|
|
|
|
y2 = d->last;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
x1 = d->grabPoint;
|
|
|
|
x2 = x;
|
|
|
|
y1 = d->last;
|
|
|
|
y2 = y;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (x2 != x1)
|
|
|
|
{
|
|
|
|
for (i = x1 ; i <= x2 ; i++)
|
|
|
|
d->curves->setCurveValue(m_channelType, i,
|
|
|
|
m_imageHistogram->getHistogramSegment()-1 - (y1 + ((y2 - y1) * (i - x1)) / (x2 - x1)));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
d->curves->setCurveValue(m_channelType, x, m_imageHistogram->getHistogramSegment()-1 - y);
|
|
|
|
}
|
|
|
|
|
|
|
|
d->grabPoint = x;
|
|
|
|
d->last = y;
|
|
|
|
}
|
|
|
|
|
|
|
|
emit signalCurvesChanged();
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
d->xMouseOver = x;
|
|
|
|
d->yMouseOver = m_imageHistogram->getHistogramSegment()-1 - y;
|
|
|
|
emit signalMouseMoved(d->xMouseOver, d->yMouseOver);
|
|
|
|
repaint(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesWidget::leaveEvent(TQEvent*)
|
|
|
|
{
|
|
|
|
d->xMouseOver = -1;
|
|
|
|
d->yMouseOver = -1;
|
|
|
|
emit signalMouseMoved(d->xMouseOver, d->yMouseOver);
|
|
|
|
repaint(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // NameSpace Digikam
|