/* * This file is part of Chalk * * Copyright (c) 2005 Casper Boemann * * 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 #include #include #include #include #include #include "kis_filter_configuration.h" #include "kis_filter_config_widget.h" #include "kis_perchannel_filter.h" #include "wdg_perchannel.h" #include "kis_colorspace.h" #include "kis_paint_device.h" #include "kis_iterators_pixel.h" #include "kcurve.h" #include "kis_histogram.h" #include "kis_basic_histogram_producers.h" #include "kis_painter.h" KisPerChannelFilterConfiguration::KisPerChannelFilterConfiguration(int n) : KisFilterConfiguration( "perchannel", 1 ) { curves = new TQSortedList >[n]; for(int i=0;i >[nTransfers]; while (!curvesNode.isNull()) { TQDomElement curvesElement = curvesNode.toElement(); if (!curvesElement.isNull() && !curvesElement.text().isEmpty()) { TQStringList data = TQStringList::split( ";", curvesElement.text() ); TQStringList::Iterator pairStart = data.begin(); TQStringList::Iterator pairEnd = data.end(); for (TQStringList::Iterator it = pairStart; it != pairEnd; ++it) { TQString pair = * it; if (pair.tqfind(",") > -1) { TQPair *p = new TQPair; p->first = pair.section(",", 0, 0).toDouble(); p->second = pair.section(",", 1, 1).toDouble(); curves[count].append(p); } } } count++; curvesNode = curvesNode.nextSibling(); } } } n = n.nextSibling(); } for(int ch = 0; ch < nTransfers; ++ch) { transfers[ch] = new TQ_UINT16[256]; for(int i = 0; i < 256; ++i) { TQ_INT32 val; val = int(0xFFFF * KCurve::getCurveValue(curves[ch], i / 255.0)); if(val > 0xFFFF) val = 0xFFFF; if(val < 0) val = 0; transfers[ch][i] = val; } } dirty = true; } TQString KisPerChannelFilterConfiguration::toString() { TQDomDocument doc = TQDomDocument("filterconfig"); TQDomElement root = doc.createElement( "filterconfig" ); root.setAttribute( "name", name() ); root.setAttribute( "version", version() ); TQDomElement c = doc.createElement("curves"); c.setAttribute("number", nTransfers); c.setAttribute("name", "curves"); for (int i = 0; i < nTransfers; ++i) { TQDomElement t = doc.createElement("curve"); TQPtrList > curve = curves[i]; TQString sCurve; TQPair * pair; for ( pair = curve.first(); pair; pair = curve.next() ) { sCurve += TQString::number(pair->first); sCurve += ","; sCurve += TQString::number(pair->second); sCurve += ";"; } TQDomText text = doc.createCDATASection(sCurve); t.appendChild(text); c.appendChild(t); } root.appendChild(c); doc.appendChild( root ); return doc.toString(); } KisFilterConfigWidget * KisPerChannelFilter::createConfigurationWidget(TQWidget *tqparent, KisPaintDeviceSP dev) { return new KisPerChannelConfigWidget(tqparent, dev); } KisFilterConfiguration* KisPerChannelFilter::configuration(TQWidget *nwidget) { KisPerChannelConfigWidget* widget = (KisPerChannelConfigWidget*)nwidget; if ( widget == 0 ) { return 0; } else { return widget->config(); } } std::list KisPerChannelFilter::listOfExamplesConfiguration(KisPaintDeviceSP dev) { std::list list; list.insert(list.begin(), new KisPerChannelFilterConfiguration(dev->colorSpace()->nColorChannels())); return list; } void KisPerChannelFilter::process(KisPaintDeviceSP src, KisPaintDeviceSP dst, KisFilterConfiguration* config, const TQRect& rect) { if (!config) { kdWarning() << "No configuration object for per-channel filter\n"; return; } KisPerChannelFilterConfiguration* configBC = dynamic_cast(config); if (configBC->nTransfers != src->colorSpace()->nColorChannels()) { // We got an illegal number of colorchannels.KisFilter return; } if (configBC->dirty || (src->colorSpace() != configBC->oldCs)) { delete configBC->adjustment; configBC->adjustment = src->colorSpace()->createPerChannelAdjustment(configBC->transfers); kdDebug() << configBC->adjustment << endl; configBC->oldCs = src->colorSpace(); configBC->dirty = false; } KisColorAdjustment *adj = configBC->adjustment; if (src!=dst) { KisPainter gc(dst); gc.bitBlt(rect.x(), rect.y(), COMPOSITE_COPY, src, rect.x(), rect.y(), rect.width(), rect.height()); gc.end(); } KisRectIteratorPixel iter = dst->createRectIterator(rect.x(), rect.y(), rect.width(), rect.height(), true ); setProgressTotalSteps(rect.width() * rect.height()); TQ_INT32 pixelsProcessed = 0; while( ! iter.isDone() && !cancelRequested()) { TQ_UINT32 npix=0, maxpix = iter.nConseqPixels(); TQ_UINT8 selectedness = iter.selectedness(); // The idea here is to handle stretches of completely selected and completely unselected pixels. // Partially selected pixels are handled one pixel at a time. switch(selectedness) { case MIN_SELECTED: while(iter.selectedness()==MIN_SELECTED && maxpix) { --maxpix; ++iter; ++npix; } pixelsProcessed += npix; break; case MAX_SELECTED: { TQ_UINT8 *firstPixel = iter.rawData(); while(iter.selectedness()==MAX_SELECTED && maxpix) { --maxpix; if (maxpix != 0) ++iter; ++npix; } // adjust src->colorSpace()->applyAdjustment(firstPixel, firstPixel, adj, npix); pixelsProcessed += npix; ++iter; break; } default: // adjust, but since it's partially selected we also only partially adjust src->colorSpace()->applyAdjustment(iter.oldRawData(), iter.rawData(), adj, 1); const TQ_UINT8 *pixels[2] = {iter.oldRawData(), iter.rawData()}; TQ_UINT8 weights[2] = {MAX_SELECTED - selectedness, selectedness}; src->colorSpace()->mixColors(pixels, weights, 2, iter.rawData()); ++iter; pixelsProcessed++; break; } setProgress(pixelsProcessed); } setProgressDone(); } void KisPerChannelConfigWidget::setActiveChannel(int ch) { int i; int height = 256; TQPixmap pix(256, height); pix.fill(); TQPainter p(&pix); p.setPen(TQPen::TQPen(TQt::gray,1, TQt::SolidLine)); m_histogram->setChannel(ch); double highest = (double)m_histogram->calculations().getHighest(); TQ_INT32 bins = m_histogram->producer()->numberOfBins(); if (m_histogram->getHistogramType() == LINEAR) { double factor = (double)height / highest; for( i=0; igetValue(i) * factor)); } } else { double factor = (double)height / (double)log(highest); for( i = 0; i < bins; ++i ) { p.drawLine(i, height, i, height - int(log((double)m_histogram->getValue(i)) * factor)); } } m_curves[m_activeCh].setAutoDelete(true); m_curves[m_activeCh] = m_page->kCurve->getCurve(); m_activeCh = ch; m_page->kCurve->setCurve(m_curves[m_activeCh]); m_page->kCurve->setPixmap(pix); } KisPerChannelConfigWidget::KisPerChannelConfigWidget(TQWidget * tqparent, KisPaintDeviceSP dev, const char * name, WFlags f) : KisFilterConfigWidget(tqparent, name, f) { int i; int height; m_page = new WdgPerChannel(this); TQHBoxLayout * l = new TQHBoxLayout(this); Q_CHECK_PTR(l); m_dev = dev; m_curves = new TQSortedList >[m_dev->colorSpace()->nColorChannels()]; m_activeCh = 0; for(unsigned int ch=0; ch colorSpace()->nColorChannels(); ch++) { m_curves[ch].append(new TQPair(0, 0)); m_curves[ch].append(new TQPair(1, 1)); } l->add(m_page); height = 256; connect( m_page->kCurve, TQT_SIGNAL(modified()), TQT_SIGNAL(sigPleaseUpdatePreview())); // Fill in the channel chooser TQValueVector channels = dev->colorSpace()->channels(); for(unsigned int val=0; val < dev->colorSpace()->nColorChannels(); val++) m_page->cmbChannel->insertItem(channels.at(val)->name()); connect( m_page->cmbChannel, TQT_SIGNAL(activated(int)), this, TQT_SLOT(setActiveChannel(int))); // Create the horizontal gradient label TQPixmap hgradientpix(256, 1); TQPainter hgp(&hgradientpix); hgp.setPen(TQPen::TQPen(TQColor(0,0,0),1, TQt::SolidLine)); for( i=0; i<256; ++i ) { hgp.setPen(TQColor(i,i,i)); hgp.drawPoint(i, 0); } m_page->hgradient->setPixmap(hgradientpix); // Create the vertical gradient label TQPixmap vgradientpix(1, 256); TQPainter vgp(&vgradientpix); vgp.setPen(TQPen::TQPen(TQColor(0,0,0),1, TQt::SolidLine)); for( i=0; i<256; ++i ) { vgp.setPen(TQColor(i,i,i)); vgp.drawPoint(0, 255-i); } m_page->vgradient->setPixmap(vgradientpix); KisIDList keys = KisHistogramProducerFactoryRegistry::instance()->listKeysCompatibleWith(m_dev->colorSpace()); KisHistogramProducerFactory *hpf; hpf = KisHistogramProducerFactoryRegistry::instance()->get(*(keys.at(0))); m_histogram = new KisHistogram(m_dev, hpf->generate(), LINEAR); setActiveChannel(0); } KisPerChannelFilterConfiguration * KisPerChannelConfigWidget::config() { int nCh = m_dev->colorSpace()->nColorChannels(); KisPerChannelFilterConfiguration * cfg = new KisPerChannelFilterConfiguration(nCh); m_curves[m_activeCh].setAutoDelete(true); m_curves[m_activeCh] = m_page->kCurve->getCurve(); for(int ch = 0; ch < nCh; ch++) { cfg->curves[ch].setAutoDelete(true); cfg->curves[ch].clear(); TQPair *p, *inpoint; inpoint = m_curves[ch].first(); while(inpoint) { p = new TQPair(inpoint->first, inpoint->second); cfg->curves[ch].append(p); inpoint = m_curves[ch].next(); } for(int i=0; i <256; i++) { TQ_INT32 val; val = int(0xFFFF * m_page->kCurve->getCurveValue(m_curves[ch], i / 255.0)); if ( val > 0xFFFF ) val = 0xFFFF; if ( val < 0 ) val = 0; cfg->transfers[ch][i] = val; } } cfg->dirty = true; return cfg; } void KisPerChannelConfigWidget::setConfiguration(KisFilterConfiguration * config) { KisPerChannelFilterConfiguration * cfg = dynamic_cast(config); for(unsigned int ch = 0; ch < cfg->nTransfers; ch++) { m_curves[ch].setAutoDelete(true); m_curves[ch].clear(); TQPair *p, *inpoint; inpoint = cfg->curves[ch].first(); while(inpoint) { p = new TQPair(inpoint->first, inpoint->second); m_curves[ch].append(p); inpoint = cfg->curves[ch].next(); } } m_page->kCurve->setCurve(m_curves[m_activeCh]); setActiveChannel( 0 ); } #include "kis_perchannel_filter.moc"