Commit 324d17b6 authored by Cyrille Berger's avatar Cyrille Berger

port the filters to use KoColorTransformationFilter

svn path=/trunk/koffice/; revision=958959
parent fc9d8046
......@@ -258,76 +258,8 @@ bool KisDesaturateFilter::workWith(const KoColorSpace* cs) const
return (cs->profile() != 0);
}
void KisDesaturateFilter::process(KisConstProcessingInformation srcInfo,
KisProcessingInformation dstInfo,
const QSize& size,
const KisFilterConfiguration* config,
KoUpdater* progressUpdater
) const
KoColorTransformation* KisDesaturateFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfiguration* config) const
{
const KisPaintDeviceSP src = srcInfo.paintDevice();
KisPaintDeviceSP dst = dstInfo.paintDevice();
QPoint dstTopLeft = dstInfo.topLeft();
QPoint srcTopLeft = srcInfo.topLeft();
Q_ASSERT(src != 0);
Q_ASSERT(dst != 0);
Q_UNUSED(config);
if (dst != src) {
KisPainter gc(dst, dstInfo.selection());
gc.bitBlt(dstTopLeft.x(), dstTopLeft.y(), COMPOSITE_COPY, src, srcTopLeft.x(), srcTopLeft.y(), size.width(), size.height());
gc.end();
}
KoColorTransformation * m_adj = src->colorSpace()->createDesaturateAdjustment();
KisRectIteratorPixel iter = dst->createRectIterator(dstTopLeft.x(), dstTopLeft.y(), size.width(), size.height(), dstInfo.selection());
qint32 totalCost = size.width() * size.height() / 100;
if (totalCost == 0) totalCost = 1;
qint32 pixelsProcessed = 0;
KoMixColorsOp * mixOp = src->colorSpace()->mixColorsOp();
while (! iter.isDone() && !(progressUpdater && progressUpdater->interrupted())) {
quint32 npix = 0, maxpix = iter.nConseqPixels();
quint8 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: {
quint8 *firstPixel = iter.rawData();
while (iter.selectedness() == MAX_SELECTED && maxpix) {
--maxpix;
if (maxpix != 0)
++iter;
++npix;
}
// adjust
m_adj->transform(firstPixel, firstPixel, npix);
pixelsProcessed += npix;
++iter;
break;
}
default:
// adjust, but since it's partially selected we also only partially adjust
m_adj->transform(iter.oldRawData(), iter.rawData(), 1);
const quint8 *pixels[2] = {iter.oldRawData(), iter.rawData()};
qint16 weights[2] = {MAX_SELECTED - selectedness, selectedness};
mixOp->mixColors(pixels, weights, 2, iter.rawData());
++iter;
pixelsProcessed++;
break;
}
if (progressUpdater) progressUpdater->setProgress(pixelsProcessed / totalCost);
}
return cs->createDesaturateAdjustment();
}
......@@ -22,6 +22,7 @@
#include <kparts/plugin.h>
#include "kis_perchannel_filter.h"
#include "filter/kis_color_transformation_filter.h"
class KoColorSpace;
class KoColorTransformation;
......@@ -33,18 +34,13 @@ public:
virtual ~ColorsFilters();
};
class KisAutoContrast : public KisFilter
class KisAutoContrast : public KisColorTransformationFilter
{
public:
KisAutoContrast();
public:
using KisFilter::process;
void process(KisConstProcessingInformation src,
KisProcessingInformation dst,
const QSize& size,
const KisFilterConfiguration* config,
KoUpdater* progressUpdater
) const;
virtual KoColorTransformation* createTransformation(const KoColorSpace* cs, const KisFilterConfiguration* config) const;
static inline KoID id() {
return KoID("autocontrast", i18n("Auto Contrast"));
}
......
......@@ -139,7 +139,7 @@ QString KisBrightnessContrastFilterConfiguration::toString()
}
KisBrightnessContrastFilter::KisBrightnessContrastFilter()
: KisFilter(id(), categoryAdjust(), i18n("&Brightness/Contrast curve..."))
: KisColorTransformationFilter(id(), categoryAdjust(), i18n("&Brightness/Contrast curve..."))
{
setSupportsPainting(true);
setSupportsPreview(true);
......@@ -165,85 +165,12 @@ bool KisBrightnessContrastFilter::workWith(const KoColorSpace* cs) const
return (cs->profile() != 0);
}
void KisBrightnessContrastFilter::process(KisConstProcessingInformation srcInfo,
KisProcessingInformation dstInfo,
const QSize& size,
const KisFilterConfiguration* config,
KoUpdater* progressUpdater
) const
KoColorTransformation* KisBrightnessContrastFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfiguration* config) const
{
const KisPaintDeviceSP src = srcInfo.paintDevice();
KisPaintDeviceSP dst = dstInfo.paintDevice();
QPoint dstTopLeft = dstInfo.topLeft();
QPoint srcTopLeft = srcInfo.topLeft();
Q_ASSERT(src != 0);
Q_ASSERT(dst != 0);
if (!config) {
warnKrita << "No configuration object for brightness/contrast filter\n";
return;
}
const KisBrightnessContrastFilterConfiguration* configBC = dynamic_cast<const KisBrightnessContrastFilterConfiguration*>( config );
Q_ASSERT(configBC);
if(!configBC) return 0;
if (src != dst) {
KisPainter gc(dst, dstInfo.selection());
gc.bitBlt(dstTopLeft.x(), dstTopLeft.y(), COMPOSITE_COPY, src, srcTopLeft.x(), srcTopLeft.y(), size.width(), size.height());
gc.end();
}
KoColorTransformation * adjustment = src->colorSpace()->createBrightnessContrastAdjustment(configBC->transfer);
KisRectIteratorPixel iter = dst->createRectIterator(srcTopLeft.x(), srcTopLeft.y(), size.width(), size.height(), dstInfo.selection());
qint32 totalCost = (size.width() * size.height()) / 100;
if (totalCost == 0) totalCost = 1;
qint32 pixelsProcessed = 0;
KoMixColorsOp * mixOp = src->colorSpace()->mixColorsOp();
while (! iter.isDone() && !(progressUpdater && progressUpdater->interrupted())) {
quint32 npix = 0, maxpix = iter.nConseqPixels();
quint8 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: {
quint8 *firstPixel = iter.rawData();
while (iter.selectedness() == MAX_SELECTED && maxpix) {
--maxpix;
if (maxpix != 0)
++iter;
++npix;
}
// adjust
adjustment->transform(firstPixel, firstPixel, npix);
pixelsProcessed += npix;
++iter;
break;
}
default:
// adjust, but since it's partially selected we also only partially adjust
adjustment->transform(iter.oldRawData(), iter.rawData(), 1);
const quint8 *pixels[2] = {iter.oldRawData(), iter.rawData()};
qint16 weights[2] = {MAX_SELECTED - selectedness, selectedness};
mixOp->mixColors(pixels, weights, 2, iter.rawData());
++iter;
pixelsProcessed++;
break;
}
if (progressUpdater) progressUpdater->setProgress(pixelsProcessed / totalCost);
}
delete adjustment;
KoColorTransformation * adjustment = cs->createBrightnessContrastAdjustment(configBC->transfer);
}
KisBrightnessContrastConfigWidget::KisBrightnessContrastConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WFlags f)
......
......@@ -23,7 +23,7 @@
#include <QList>
#include "filter/kis_filter.h"
#include "filter/kis_color_transformation_filter.h"
#include "kis_config_widget.h"
#include "ui_wdg_brightness_contrast.h"
#include <filter/kis_filter_configuration.h>
......@@ -31,6 +31,7 @@
#include <kis_paint_device.h>
#include <kis_processing_information.h>
#include <KoProgressUpdater.h>
class QWidget;
class KoColorTransformation;
......@@ -63,7 +64,7 @@ public:
/**
* This class affect Intensity Y of the image
*/
class KisBrightnessContrastFilter : public KisFilter
class KisBrightnessContrastFilter : public KisColorTransformationFilter
{
public:
......@@ -73,12 +74,8 @@ public:
public:
using KisFilter::process;
void process(KisConstProcessingInformation src,
KisProcessingInformation dst,
const QSize& size,
const KisFilterConfiguration* config,
KoUpdater* progressUpdater
) const;
virtual KoColorTransformation* createTransformation(const KoColorSpace* cs, const KisFilterConfiguration* config) const;
static inline KoID id() {
return KoID("brightnesscontrast", i18n("Brightness / Contrast"));
}
......
......@@ -28,7 +28,7 @@
#include <kis_processing_information.h>
KisHSVAdjustmentFilter::KisHSVAdjustmentFilter()
: KisFilter(id(), categoryAdjust(), i18n("&HSV Adjustment..."))
: KisColorTransformationFilter(id(), categoryAdjust(), i18n("&HSV Adjustment..."))
{
setSupportsPainting(true);
setSupportsPreview(true);
......@@ -42,51 +42,15 @@ KisConfigWidget * KisHSVAdjustmentFilter::createConfigurationWidget(QWidget* par
return new KisHSVConfigWidget(parent);
}
void KisHSVAdjustmentFilter::process(KisConstProcessingInformation srcInfo,
KisProcessingInformation dstInfo,
const QSize& size,
const KisFilterConfiguration* config,
KoUpdater* progressUpdater
) const
KoColorTransformation* KisHSVAdjustmentFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfiguration* config) const
{
const KisPaintDeviceSP src = srcInfo.paintDevice();
KisPaintDeviceSP dst = dstInfo.paintDevice();
QPoint dstTopLeft = dstInfo.topLeft();
QPoint srcTopLeft = srcInfo.topLeft();
Q_ASSERT(src != 0);
Q_ASSERT(dst != 0);
QHash<QString, QVariant> params;
if (config) {
params["h"] = config->getInt("h", 0) / 180.0;
params["s"] = config->getInt("s", 0) * 0.01;
params["v"] = config->getInt("v", 0) * 0.01;
}
KoColorTransformation* transfo = src->colorSpace()->createColorTransformation("hsv_adjustment", params);
if (!transfo) {
kError() << "hsv_adjustment transformation is unavailable, go check your installation";
return;
}
int count = 0;
int cost = size.width() * size.height();
if (cost == 0) cost = 1;
KisHLineConstIteratorPixel srcIt = src->createHLineConstIterator(srcTopLeft.x(), srcTopLeft.y(), size.width());
KisHLineIteratorPixel dstIt = dst->createHLineIterator(dstTopLeft.x(), dstTopLeft.y(), size.width());
for (int row = 0; row < size.height(); ++row) {
while (!srcIt.isDone()) {
if (srcIt.isSelected()) {
transfo->transform(srcIt.oldRawData(), dstIt.rawData(), 1);
}
++srcIt;
++dstIt;
if (progressUpdater) progressUpdater->setProgress((++count) / cost);
}
srcIt.nextRow();
dstIt.nextRow();
}
delete transfo;
return cs->createColorTransformation("hsv_adjustment", params);
}
KisFilterConfiguration* KisHSVAdjustmentFilter::factoryConfiguration(const KisPaintDeviceSP) const
......
......@@ -26,6 +26,7 @@
#include "filter/kis_filter.h"
#include "kis_config_widget.h"
#include "ui_wdg_hsv_adjustment.h"
#include "filter/kis_color_transformation_filter.h"
class QWidget;
class KoColorTransformation;
......@@ -33,7 +34,7 @@ class KoColorTransformation;
/**
* This class affect Intensity Y of the image
*/
class KisHSVAdjustmentFilter : public KisFilter
class KisHSVAdjustmentFilter : public KisColorTransformationFilter
{
public:
......@@ -46,12 +47,8 @@ public:
using KisFilter::process;
void process(KisConstProcessingInformation src,
KisProcessingInformation dst,
const QSize& size,
const KisFilterConfiguration* config,
KoUpdater* progressUpdater
) const;
virtual KoColorTransformation* createTransformation(const KoColorSpace* cs, const KisFilterConfiguration* config) const;
static inline KoID id() {
return KoID("hsvadjustment", i18n("HSV Adjustment"));
}
......
......@@ -460,7 +460,7 @@ void KisPerChannelFilterConfiguration::toXML(QDomDocument& doc, QDomElement& roo
//void KisPerChannelFilterConfiguration::toXML()
KisPerChannelFilter::KisPerChannelFilter() : KisFilter(id(), categoryAdjust(), i18n("&Color Adjustment curves..."))
KisPerChannelFilter::KisPerChannelFilter() : KisColorTransformationFilter(id(), categoryAdjust(), i18n("&Color Adjustment curves..."))
{
setSupportsPainting(true);
setSupportsPreview(true);
......@@ -479,94 +479,17 @@ KisFilterConfiguration * KisPerChannelFilter::factoryConfiguration(const KisPain
return new KisPerChannelFilterConfiguration(0);
}
void KisPerChannelFilter::process(KisConstProcessingInformation srcInfo,
KisProcessingInformation dstInfo,
const QSize& size,
const KisFilterConfiguration* config,
KoUpdater* progressUpdater
) const
KoColorTransformation* KisPerChannelFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfiguration* config) const
{
const KisPaintDeviceSP src = srcInfo.paintDevice();
KisPaintDeviceSP dst = dstInfo.paintDevice();
QPoint dstTopLeft = dstInfo.topLeft();
QPoint srcTopLeft = srcInfo.topLeft();
Q_ASSERT(src != 0);
Q_ASSERT(dst != 0);
if (!config) {
warnKrita << "No configuration object for per-channel filter";
return;
}
KisPerChannelFilterConfiguration* configBC =
const_cast<KisPerChannelFilterConfiguration*>(dynamic_cast<const KisPerChannelFilterConfiguration*>(config)); // Somehow, this shouldn't happen
if (not configBC)
return;
if (configBC->m_nTransfers != src->colorSpace()->colorChannelCount()) {
Q_ASSERT(configBC);
if (configBC->m_nTransfers != cs->colorChannelCount()) {
// We got an illegal number of colorchannels.KisFilter
return;
}
KoColorTransformation *adj = src->colorSpace()->createPerChannelAdjustment(configBC->m_transfers);
if (src != dst) {
KisPainter gc(dst, dstInfo.selection());
gc.bitBlt(dstTopLeft.x(), dstTopLeft.y(), COMPOSITE_COPY, src, srcTopLeft.x(), srcTopLeft.y(), size.width(), size.height());
gc.end();
}
KisRectIteratorPixel iter = dst->createRectIterator(dstTopLeft.x(), dstTopLeft.y(), size.width(), size.height(), dstInfo.selection());
KoMixColorsOp * mixOp = src->colorSpace()->mixColorsOp();
qint32 totalCost = (size.width() * size.height()) / 100;
if (totalCost == 0) totalCost = 1;
qint32 pixelsProcessed = 0;
while (!iter.isDone() && (progressUpdater && !progressUpdater->interrupted())) {
quint32 npix = 0, maxpix = iter.nConseqPixels();
quint8 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: {
quint8 *firstPixel = iter.rawData();
while (iter.selectedness() == MAX_SELECTED && maxpix) {
--maxpix;
if (maxpix != 0)
++iter;
++npix;
}
// adjust
adj->transform(firstPixel, firstPixel, npix);
pixelsProcessed += npix;
++iter;
break;
}
default:
// adjust, but since it's partially selected we also only partially adjust
adj->transform(iter.oldRawData(), iter.rawData(), 1);
const quint8 *pixels[2] = {iter.oldRawData(), iter.rawData()};
qint16 weights[2] = {MAX_SELECTED - selectedness, selectedness};
mixOp->mixColors(pixels, weights, 2, iter.rawData());
++iter;
pixelsProcessed++;
break;
}
if (progressUpdater) progressUpdater->setProgress(pixelsProcessed / totalCost);
return 0;
}
return cs->createPerChannelAdjustment(configBC->m_transfers);
}
#include "kis_perchannel_filter.moc"
......@@ -24,7 +24,7 @@
#include <QPair>
#include <QList>
#include "filter/kis_filter.h"
#include "filter/kis_color_transformation_filter.h"
#include "filter/kis_filter_configuration.h"
#include "kis_config_widget.h"
......@@ -82,7 +82,7 @@ protected:
* This class is generic for filters that affect channel separately
*/
class KisPerChannelFilter
: public KisFilter
: public KisColorTransformationFilter
{
public:
KisPerChannelFilter();
......@@ -90,13 +90,8 @@ public:
virtual KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, const KisImageSP image = 0) const;
virtual KisFilterConfiguration * factoryConfiguration(const KisPaintDeviceSP) const;
using KisFilter::process;
void process(KisConstProcessingInformation src,
KisProcessingInformation dst,
const QSize& size,
const KisFilterConfiguration* config,
KoUpdater* progressUpdater
) const;
virtual KoColorTransformation* createTransformation(const KoColorSpace* cs, const KisFilterConfiguration* config) const;
static inline KoID id() {
return KoID("perchannel", i18n("Color Adjustment"));
}
......
......@@ -32,13 +32,9 @@
#include <kis_debug.h>
#include <kgenericfactory.h>
#include <KoColorTransformation.h>
#include <KoProgressUpdater.h>
#include <kis_processing_information.h>
#include <kis_types.h>
#include <kis_selection.h>
#include <kis_image.h>
#include <kis_iterators_pixel.h>
#include <kis_layer.h>
#include <filter/kis_filter_registry.h>
......@@ -59,7 +55,7 @@ KritaExample::~KritaExample()
{
}
KisFilterInvert::KisFilterInvert() : KisFilter(id(), categoryAdjust(), i18n("&Invert"))
KisFilterInvert::KisFilterInvert() : KisColorTransformationFilter(id(), categoryAdjust(), i18n("&Invert"))
{
setColorSpaceIndependence(FULLY_INDEPENDENT);
setSupportsPainting(true);
......@@ -67,108 +63,8 @@ KisFilterInvert::KisFilterInvert() : KisFilter(id(), categoryAdjust(), i18n("&In
setSupportsIncrementalPainting(false);
}
void KisFilterInvert::process(KisConstProcessingInformation srcInfo,
KisProcessingInformation dstInfo,
const QSize& size,
const KisFilterConfiguration* config,
KoUpdater* progressUpdater
) const
KoColorTransformation* KisFilterInvert::createTransformation(const KoColorSpace* cs, const KisFilterConfiguration* config) const
{
const KisPaintDeviceSP src = srcInfo.paintDevice();
KisPaintDeviceSP dst = dstInfo.paintDevice();
QPoint dstTopLeft = dstInfo.topLeft();
QPoint srcTopLeft = srcInfo.topLeft();
Q_UNUSED(config);
Q_ASSERT(!src.isNull());
Q_ASSERT(!dst.isNull());
if (progressUpdater) {
progressUpdater->setRange(0, size.height());
}
const KoColorSpace * cs = src->colorSpace();
KoColorTransformation* inverter = cs->createInvertTransformation();
QTime t;
t.start();
// Method one: iterate and check every pixel for selectedness. It is
// only slightly slower than the next method and the code is very
// clear. Note that using nextRow() instead of recreating the iterators
// for every row makes a huge difference.
KisHLineConstIteratorPixel srcIt = src->createHLineConstIterator(srcTopLeft.x(), srcTopLeft.y(), size.width(), srcInfo.selection());
KisHLineIteratorPixel dstIt = dst->createHLineIterator(dstTopLeft.x(), dstTopLeft.y(), size.width(), dstInfo.selection());
for (int row = 0; row < size.height() && !(progressUpdater && progressUpdater->interrupted()); ++row) {
while (!srcIt.isDone() && !(progressUpdater && progressUpdater->interrupted())) {
if (srcIt.isSelected()) {
inverter->transform(srcIt.oldRawData(), dstIt.rawData(), 1);
}
++srcIt;
++dstIt;
}
srcIt.nextRow();
dstIt.nextRow();
if (progressUpdater) progressUpdater->setValue(row);
}
dbgPlugins << "Per-pixel isSelected():" << t.elapsed() << " ms";
#if 0
t.restart();
bool hasSelection = srcInfo.selection();
// Method two: check the number of consecutive pixels the iterators
// points to. Take as large stretches of unselected pixels as possible
// and pass those to the color space transform object in one go. It's
// quite a bit speedier, with the speed improvement more noticeable
// the less happens inside the color transformation.
srcIt = src->createHLineConstIterator(srcTopLeft.x(), srcTopLeft.y(), size.width());
dstIt = dst->createHLineIterator(dstTopLeft.x(), dstTopLeft.y(), size.width());
for (int row = 0; row < size.height(); ++row) {
while (! srcIt.isDone()) {
int srcItConseq = srcIt.nConseqHPixels();
int dstItConseq = srcIt.nConseqHPixels();
int conseqPixels = qMin(srcItConseq, dstItConseq);
int pixels = 0;
if (hasSelection) {
// Get largest horizontal row of selected pixels
while (srcIt.isSelected() && pixels < conseqPixels) {
++pixels;
}
inverter->transform(srcIt.oldRawData(), dstIt.rawData(), pixels);
// We apparently found a non-selected pixels, or the row
// was done; get the stretch of non-selected pixels
while (!srcIt.isSelected() && pixels < conseqPixels) {
++ pixels;
}
} else {
pixels = conseqPixels;
inverter->transform(srcIt.oldRawData(), dstIt.rawData(), pixels);
}
// Update progress
srcIt += pixels;
dstIt += pixels;
}
srcIt.nextRow();
dstIt.nextRow();
}
dbgPlugins << "Consecutive pixels:" << t.elapsed() << " ms";
#endif
delete inverter;
//if(progressUpdater) progressUpdater->setProgress( 100 );
// Two inversions make no inversion? No -- because we're reading