|
|
|
/*
|
|
|
|
* Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be>
|
|
|
|
*
|
|
|
|
* 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 of the License, 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.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
#include <tqpainter.h>
|
|
|
|
#include <tqpixmap.h>
|
|
|
|
#include <tqlabel.h>
|
|
|
|
#include <tqcombobox.h>
|
|
|
|
#include <tqbuttongroup.h>
|
|
|
|
#include <tqpushbutton.h>
|
|
|
|
#include <tqscrollbar.h>
|
|
|
|
|
|
|
|
#include <kdebug.h>
|
|
|
|
|
|
|
|
#include "kis_channelinfo.h"
|
|
|
|
#include "kis_histogram.h"
|
|
|
|
#include "kis_global.h"
|
|
|
|
#include "kis_types.h"
|
|
|
|
#include "kis_layer.h"
|
|
|
|
#include "kis_colorspace.h"
|
|
|
|
#include "kis_histogram_view.h"
|
|
|
|
#include "kis_basic_histogram_producers.h"
|
|
|
|
#include "kis_paint_device.h"
|
|
|
|
|
|
|
|
KisHistogramView::KisHistogramView(TQWidget *parent, const char *name, WFlags f)
|
|
|
|
: TQLabel(parent, name, f)
|
|
|
|
{
|
|
|
|
// This is needed until we can computationally scale it well. Until then, this is needed
|
|
|
|
// And when we have it, it won't hurt to have it around
|
|
|
|
setScaledContents(true);
|
|
|
|
setFrameShape(TQFrame::Box); // Draw a box around ourselves
|
|
|
|
}
|
|
|
|
|
|
|
|
KisHistogramView::~KisHistogramView()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void KisHistogramView::setPaintDevice(KisPaintDeviceSP dev)
|
|
|
|
{
|
|
|
|
m_cs = dev->colorSpace();
|
|
|
|
|
|
|
|
setChannels(); // Sets m_currentProducer to the first in the list
|
|
|
|
|
|
|
|
if (!m_currentProducer)
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_from = m_currentProducer->viewFrom();
|
|
|
|
m_width = m_currentProducer->viewWidth();
|
|
|
|
|
|
|
|
m_histogram = new KisHistogram(dev, m_currentProducer, LINEAR);
|
|
|
|
|
|
|
|
updateHistogram();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KisHistogramView::setHistogram(KisHistogramSP histogram)
|
|
|
|
{
|
|
|
|
m_cs = 0;
|
|
|
|
m_histogram = histogram;
|
|
|
|
m_currentProducer = m_histogram->producer();
|
|
|
|
m_from = m_currentProducer->viewFrom();
|
|
|
|
m_width = m_currentProducer->viewWidth();
|
|
|
|
|
|
|
|
m_comboInfo.clear();
|
|
|
|
m_channelStrings.clear();
|
|
|
|
m_channels.clear();
|
|
|
|
m_channelToOffset.clear();
|
|
|
|
|
|
|
|
addProducerChannels(m_currentProducer);
|
|
|
|
|
|
|
|
// Set the currently viewed channel:
|
|
|
|
m_color = false;
|
|
|
|
m_channels.append(m_comboInfo.at(1).channel);
|
|
|
|
m_channelToOffset.append(0);
|
|
|
|
|
|
|
|
updateHistogram();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KisHistogramView::setView(double from, double size)
|
|
|
|
{
|
|
|
|
m_from = from;
|
|
|
|
m_width = size;
|
|
|
|
if (m_from + m_width > 1.0)
|
|
|
|
m_from = 1.0 - m_width;
|
|
|
|
m_histogram->producer()->setView(m_from, m_width);
|
|
|
|
|
|
|
|
m_histogram->updateHistogram();
|
|
|
|
updateHistogram();
|
|
|
|
}
|
|
|
|
|
|
|
|
KisHistogramProducerSP KisHistogramView::currentProducer()
|
|
|
|
{
|
|
|
|
return m_currentProducer;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQStringList KisHistogramView::channelStrings()
|
|
|
|
{
|
|
|
|
return m_channelStrings;
|
|
|
|
}
|
|
|
|
|
|
|
|
KisIDList KisHistogramView::listProducers()
|
|
|
|
{
|
|
|
|
if (m_cs)
|
|
|
|
return KisHistogramProducerFactoryRegistry::instance()->listKeysCompatibleWith(m_cs);
|
|
|
|
return KisIDList();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KisHistogramView::setCurrentChannels(const KisID& producerID, TQValueVector<KisChannelInfo *> channels)
|
|
|
|
{
|
|
|
|
setCurrentChannels(
|
|
|
|
KisHistogramProducerFactoryRegistry::instance()->get(producerID)->generate(),
|
|
|
|
channels);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KisHistogramView::setCurrentChannels(KisHistogramProducerSP producer, TQValueVector<KisChannelInfo *> channels)
|
|
|
|
{
|
|
|
|
m_currentProducer = producer;
|
|
|
|
m_currentProducer->setView(m_from, m_width);
|
|
|
|
m_histogram->setProducer(m_currentProducer);
|
|
|
|
m_histogram->updateHistogram();
|
|
|
|
m_histogram->setChannel(0); // Set a default channel, just being nice
|
|
|
|
|
|
|
|
m_channels.clear();
|
|
|
|
m_channelToOffset.clear();
|
|
|
|
|
|
|
|
if (channels.count() == 0) {
|
|
|
|
updateHistogram();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQValueVector<KisChannelInfo *> producerChannels = m_currentProducer->channels();
|
|
|
|
|
|
|
|
for (uint i = 0; i < channels.count(); i++) {
|
|
|
|
// Also makes sure the channel is actually in the producer's list
|
|
|
|
for (uint j = 0; j < producerChannels.count(); j++) {
|
|
|
|
if (channels.at(i)->name() == producerChannels.at(j)->name()) {
|
|
|
|
m_channelToOffset.append(m_channels.count()); // The first we append maps to 0
|
|
|
|
m_channels.append(channels.at(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
updateHistogram();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KisHistogramView::hasColor()
|
|
|
|
{
|
|
|
|
return m_color;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KisHistogramView::setColor(bool set)
|
|
|
|
{
|
|
|
|
if (set != m_color) {
|
|
|
|
m_color = set;
|
|
|
|
updateHistogram();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KisHistogramView::setActiveChannel(int channel)
|
|
|
|
{
|
|
|
|
ComboboxInfo info = m_comboInfo.at(channel);
|
|
|
|
if (info.producer.data() != m_currentProducer.data()) {
|
|
|
|
m_currentProducer = info.producer;
|
|
|
|
m_currentProducer->setView(m_from, m_width);
|
|
|
|
m_histogram->setProducer(m_currentProducer);
|
|
|
|
m_histogram->updateHistogram();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_channels.clear();
|
|
|
|
m_channelToOffset.clear();
|
|
|
|
|
|
|
|
if (!m_currentProducer) {
|
|
|
|
updateHistogram();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info.isProducer) {
|
|
|
|
m_color = true;
|
|
|
|
m_channels = m_currentProducer->channels();
|
|
|
|
for (uint i = 0; i < m_channels.count(); i++)
|
|
|
|
m_channelToOffset.append(i);
|
|
|
|
m_histogram->setChannel(0); // Set a default channel, just being nice
|
|
|
|
} else {
|
|
|
|
m_color = false;
|
|
|
|
TQValueVector<KisChannelInfo *> channels = m_currentProducer->channels();
|
|
|
|
for (uint i = 0; i < channels.count(); i++) {
|
|
|
|
KisChannelInfo* channel = channels.at(i);
|
|
|
|
if (channel->name() == info.channel->name()) {
|
|
|
|
m_channels.append(channel);
|
|
|
|
m_channelToOffset.append(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
updateHistogram();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KisHistogramView::setHistogramType(enumHistogramType type)
|
|
|
|
{
|
|
|
|
m_histogram->setHistogramType(type);
|
|
|
|
updateHistogram();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KisHistogramView::setChannels()
|
|
|
|
{
|
|
|
|
m_comboInfo.clear();
|
|
|
|
m_channelStrings.clear();
|
|
|
|
m_channels.clear();
|
|
|
|
m_channelToOffset.clear();
|
|
|
|
|
|
|
|
KisIDList list = KisHistogramProducerFactoryRegistry::instance()->listKeysCompatibleWith(m_cs);
|
|
|
|
|
|
|
|
if (list.count() == 0) {
|
|
|
|
// XXX: No native histogram for this colorspace. Using converted RGB. We should have a warning
|
|
|
|
KisGenericRGBHistogramProducerFactory f;
|
|
|
|
addProducerChannels(f.generate());
|
|
|
|
} else {
|
|
|
|
for (uint i = 0; i < list.count(); i++) {
|
|
|
|
KisID id(*(list.at(i)));
|
|
|
|
addProducerChannels( KisHistogramProducerFactoryRegistry::instance()->get(id)->generate() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_currentProducer = m_comboInfo.at(0).producer;
|
|
|
|
m_color = false;
|
|
|
|
// The currently displayed channel and its offset
|
|
|
|
m_channels.append(m_comboInfo.at(1).channel);
|
|
|
|
m_channelToOffset.append(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KisHistogramView::addProducerChannels(KisHistogramProducerSP producer) {
|
|
|
|
ComboboxInfo info;
|
|
|
|
info.isProducer = true;
|
|
|
|
info.producer = producer;
|
|
|
|
// channel not used for a producer
|
|
|
|
TQValueVector<KisChannelInfo *> channels = info.producer->channels();
|
|
|
|
int count = channels.count();
|
|
|
|
m_comboInfo.append(info);
|
|
|
|
m_channelStrings.append(producer->id() . name());
|
|
|
|
for (int j = 0; j < count; j++) {
|
|
|
|
info.isProducer = false;
|
|
|
|
info.channel = channels.at(j);
|
|
|
|
m_comboInfo.append(info);
|
|
|
|
m_channelStrings.append(TQString(" ").append(info.channel->name()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KisHistogramView::updateHistogram()
|
|
|
|
{
|
|
|
|
TQ_UINT32 height = this->height();
|
|
|
|
int selFrom, selTo; // from - to in bins
|
|
|
|
|
|
|
|
if (!m_currentProducer) { // Something's very wrong: no producer for this colorspace to update histogram with!
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQ_INT32 bins = m_histogram->producer()->numberOfBins();
|
|
|
|
m_pix = TQPixmap(bins, height);
|
|
|
|
m_pix.fill();
|
|
|
|
TQPainter p(&m_pix);
|
|
|
|
p.setBrush(TQt::black);
|
|
|
|
|
|
|
|
// Draw the box of the selection, if any
|
|
|
|
if (m_histogram->hasSelection()) {
|
|
|
|
double width = m_histogram->selectionTo() - m_histogram->selectionFrom();
|
|
|
|
double factor = static_cast<double>(bins) / m_histogram->producer()->viewWidth();
|
|
|
|
selFrom = static_cast<int>(m_histogram->selectionFrom() * factor);
|
|
|
|
selTo = selFrom + static_cast<int>(width * factor);
|
|
|
|
p.drawRect(selFrom, 0, selTo - selFrom, height);
|
|
|
|
} else {
|
|
|
|
// We don't want the drawing to think we're in a selected area
|
|
|
|
selFrom = -1;
|
|
|
|
selTo = 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQ_INT32 i = 0;
|
|
|
|
double highest = 0;
|
|
|
|
bool blackOnBlack = false;
|
|
|
|
|
|
|
|
// First we iterate once, so that we have the overall maximum. This is a bit inefficient,
|
|
|
|
// but not too much since the histogram caches the calculations
|
|
|
|
for (uint chan = 0; chan < m_channels.count(); chan++) {
|
|
|
|
m_histogram->setChannel(m_channelToOffset.at(chan));
|
|
|
|
if ((double)m_histogram->calculations().getHighest() > highest)
|
|
|
|
highest = (double)m_histogram->calculations().getHighest();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint chan = 0; chan < m_channels.count(); chan++) {
|
|
|
|
TQColor color;
|
|
|
|
m_histogram->setChannel(m_channelToOffset.at(chan));
|
|
|
|
|
|
|
|
if (m_color) {
|
|
|
|
color = m_channels.at(chan)->color();
|
|
|
|
p.setPen(color);
|
|
|
|
} else {
|
|
|
|
color = TQt::black;
|
|
|
|
}
|
|
|
|
blackOnBlack = (color == TQt::black);
|
|
|
|
|
|
|
|
if (m_histogram->getHistogramType() == LINEAR) {
|
|
|
|
double factor = (double)height / highest;
|
|
|
|
for( i=0; i<bins; ++i ) {
|
|
|
|
// So that we get a good view even with a selection box with
|
|
|
|
// black colors on background of black selection
|
|
|
|
if (i >= selFrom && i < selTo && blackOnBlack) {
|
|
|
|
p.setPen(TQt::white);
|
|
|
|
} else {
|
|
|
|
p.setPen(color);
|
|
|
|
}
|
|
|
|
p.drawLine(i, height, i, height - int(m_histogram->getValue(i) * factor));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
double factor = (double)height / (double)log(highest);
|
|
|
|
for( i = 0; i < bins; ++i ) {
|
|
|
|
// Same as above
|
|
|
|
if (i >= selFrom && i < selTo && blackOnBlack) {
|
|
|
|
p.setPen(TQt::white);
|
|
|
|
} else {
|
|
|
|
p.setPen(color);
|
|
|
|
}
|
|
|
|
p.drawLine(i, height, i,
|
|
|
|
height - int(log((double)m_histogram->getValue(i)) * factor));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setPixmap(m_pix);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KisHistogramView::mousePressEvent(TQMouseEvent * e) {
|
|
|
|
if (e->button() == TQt::RightButton)
|
|
|
|
emit rightClicked(e->globalPos());
|
|
|
|
else
|
|
|
|
TQLabel::mousePressEvent(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#include "kis_histogram_view.moc"
|