|
|
|
/*
|
|
|
|
* This file is part of Chalk
|
|
|
|
*
|
|
|
|
* Copyright (c) 2005 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
|
|
|
|
*
|
|
|
|
* ported from Gimp, Copyright (C) 1997 Eiichi Takamori <taka@ma1.seikyou.ne.jp>
|
|
|
|
* original pixelize.c for GIMP 0.54 by Tracy Scott
|
|
|
|
*
|
|
|
|
* 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 <stdlib.h>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include <tqpoint.h>
|
|
|
|
#include <tqspinbox.h>
|
|
|
|
|
|
|
|
#include <tdelocale.h>
|
|
|
|
#include <kiconloader.h>
|
|
|
|
#include <kinstance.h>
|
|
|
|
#include <tdemessagebox.h>
|
|
|
|
#include <kstandarddirs.h>
|
|
|
|
#include <tdetempfile.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <kgenericfactory.h>
|
|
|
|
#include <knuminput.h>
|
|
|
|
|
|
|
|
#include <kis_doc.h>
|
|
|
|
#include <kis_image.h>
|
|
|
|
#include <kis_iterators_pixel.h>
|
|
|
|
#include <kis_layer.h>
|
|
|
|
#include <kis_filter_registry.h>
|
|
|
|
#include <kis_global.h>
|
|
|
|
#include <kis_types.h>
|
|
|
|
#include <kis_progress_display_interface.h>
|
|
|
|
|
|
|
|
#include "kis_multi_bool_filter_widget.h"
|
|
|
|
#include "kis_sobel_filter.h"
|
|
|
|
|
|
|
|
#define MIN(a,b) (((a)<(b))?(a):(b))
|
|
|
|
|
|
|
|
void KisSobelFilterConfiguration::fromXML(const TQString & s)
|
|
|
|
{
|
|
|
|
KisFilterConfiguration::fromXML(s);
|
|
|
|
m_doHorizontally = getBool( "doHorizontally" );
|
|
|
|
m_doVertically = getBool( "doVertically" );
|
|
|
|
m_keepSign = getBool( "makeOpaque" );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KisSobelFilterConfiguration::toString()
|
|
|
|
{
|
|
|
|
m_properties.clear();
|
|
|
|
setProperty("doHorizontally", m_doHorizontally);
|
|
|
|
setProperty("doVertically", m_doVertically);
|
|
|
|
setProperty("keepSign", m_keepSign);
|
|
|
|
setProperty("makeOpaque", m_makeOpaque);
|
|
|
|
|
|
|
|
return KisFilterConfiguration::toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
KisSobelFilter::KisSobelFilter() : KisFilter(id(), "edge", i18n("&Sobel..."))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void KisSobelFilter::process(KisPaintDeviceSP src, KisPaintDeviceSP dst, KisFilterConfiguration* configuration, const TQRect& rect)
|
|
|
|
{
|
|
|
|
//read the filter configuration values from the KisFilterConfiguration object
|
|
|
|
bool doHorizontally = ((KisSobelFilterConfiguration*)configuration)->doHorizontally();
|
|
|
|
bool doVertically = ((KisSobelFilterConfiguration*)configuration)->doVertically();
|
|
|
|
bool keepSign = ((KisSobelFilterConfiguration*)configuration)->keepSign();
|
|
|
|
bool makeOpaque = ((KisSobelFilterConfiguration*)configuration)->makeOpaque();
|
|
|
|
|
|
|
|
//pixelize(src, dst, x, y, width, height, pixelWidth, pixelHeight);
|
|
|
|
sobel(rect, src, dst, doHorizontally, doVertically, keepSign, makeOpaque);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KisSobelFilter::prepareRow (KisPaintDeviceSP src, TQ_UINT8* data, TQ_UINT32 x, TQ_UINT32 y, TQ_UINT32 w, TQ_UINT32 h)
|
|
|
|
{
|
|
|
|
if (y > h -1) y = h -1;
|
|
|
|
TQ_UINT32 pixelSize = src->pixelSize();
|
|
|
|
|
|
|
|
src->readBytes( data, x, y, w, 1 );
|
|
|
|
|
|
|
|
for (TQ_UINT32 b = 0; b < pixelSize; b++) {
|
|
|
|
int offset = pixelSize - b;
|
|
|
|
data[-offset] = data[b];
|
|
|
|
data[w * pixelSize + b] = data[(w - 1) * pixelSize + b];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define RMS(a, b) (sqrt ((a) * (a) + (b) * (b)))
|
|
|
|
#define ROUND(x) ((int) ((x) + 0.5))
|
|
|
|
|
|
|
|
void KisSobelFilter::sobel(const TQRect & rc, KisPaintDeviceSP src, KisPaintDeviceSP dst, bool doHorizontal, bool doVertical, bool keepSign, bool makeOpaque)
|
|
|
|
{
|
|
|
|
TQRect rect = rc; //src->exactBounds();
|
|
|
|
TQ_UINT32 x = rect.x();
|
|
|
|
TQ_UINT32 y = rect.y();
|
|
|
|
TQ_UINT32 width = rect.width();
|
|
|
|
TQ_UINT32 height = rect.height();
|
|
|
|
TQ_UINT32 pixelSize = src->pixelSize();
|
|
|
|
|
|
|
|
setProgressTotalSteps( height );
|
|
|
|
setProgressStage(i18n("Applying sobel filter..."),0);
|
|
|
|
|
|
|
|
/* allocate row buffers */
|
|
|
|
TQ_UINT8* prevRow = new TQ_UINT8[ (width + 2) * pixelSize];
|
|
|
|
TQ_CHECK_PTR(prevRow);
|
|
|
|
TQ_UINT8* curRow = new TQ_UINT8[ (width + 2) * pixelSize];
|
|
|
|
TQ_CHECK_PTR(curRow);
|
|
|
|
TQ_UINT8* nextRow = new TQ_UINT8[ (width + 2) * pixelSize];
|
|
|
|
TQ_CHECK_PTR(nextRow);
|
|
|
|
TQ_UINT8* dest = new TQ_UINT8[ width * pixelSize];
|
|
|
|
TQ_CHECK_PTR(dest);
|
|
|
|
|
|
|
|
TQ_UINT8* pr = prevRow + pixelSize;
|
|
|
|
TQ_UINT8* cr = curRow + pixelSize;
|
|
|
|
TQ_UINT8* nr = nextRow + pixelSize;
|
|
|
|
|
|
|
|
prepareRow (src, pr, x, y - 1, width, height);
|
|
|
|
prepareRow (src, cr, x, y, width, height);
|
|
|
|
|
|
|
|
TQ_UINT32 counter =0;
|
|
|
|
TQ_UINT8* d;
|
|
|
|
TQ_UINT8* tmp;
|
|
|
|
TQ_INT32 gradient, horGradient, verGradient;
|
|
|
|
// loop through the rows, applying the sobel convolution
|
|
|
|
for (TQ_UINT32 row = 0; row < height; row++)
|
|
|
|
{
|
|
|
|
|
|
|
|
// prepare the next row
|
|
|
|
prepareRow (src, nr, x, row + 1, width, height);
|
|
|
|
d = dest;
|
|
|
|
|
|
|
|
for (TQ_UINT32 col = 0; col < width * pixelSize; col++)
|
|
|
|
{
|
|
|
|
int positive = col + pixelSize;
|
|
|
|
int negative = col - pixelSize;
|
|
|
|
horGradient = (doHorizontal ?
|
|
|
|
((pr[negative] + 2 * pr[col] + pr[positive]) -
|
|
|
|
(nr[negative] + 2 * nr[col] + nr[positive]))
|
|
|
|
: 0);
|
|
|
|
|
|
|
|
verGradient = (doVertical ?
|
|
|
|
((pr[negative] + 2 * cr[negative] + nr[negative]) -
|
|
|
|
(pr[positive] + 2 * cr[positive] + nr[positive]))
|
|
|
|
: 0);
|
|
|
|
gradient = (TQ_INT32)((doVertical && doHorizontal) ?
|
|
|
|
(ROUND (RMS (horGradient, verGradient)) / 5.66) // always >0
|
|
|
|
: (keepSign ? (127 + (ROUND ((horGradient + verGradient) / 8.0)))
|
|
|
|
: (ROUND (TQABS (horGradient + verGradient) / 4.0))));
|
|
|
|
|
|
|
|
*d++ = gradient;
|
|
|
|
if (gradient > 10) counter ++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// shuffle the row pointers
|
|
|
|
tmp = pr;
|
|
|
|
pr = cr;
|
|
|
|
cr = nr;
|
|
|
|
nr = tmp;
|
|
|
|
|
|
|
|
//store the dest
|
|
|
|
dst->writeBytes(dest, x, row, width, 1);
|
|
|
|
|
|
|
|
if ( makeOpaque )
|
|
|
|
{
|
|
|
|
KisHLineIteratorPixel dstIt = dst->createHLineIterator(x, row, width, true);
|
|
|
|
while( ! dstIt.isDone() )
|
|
|
|
{
|
|
|
|
dstIt.rawData()[pixelSize-1]=255; //XXXX: is the alpha channel always 8 bit? Otherwise this is wrong!
|
|
|
|
++dstIt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
setProgress(row);
|
|
|
|
}
|
|
|
|
setProgressDone();
|
|
|
|
|
|
|
|
delete[] prevRow;
|
|
|
|
delete[] curRow;
|
|
|
|
delete[] nextRow;
|
|
|
|
delete[] dest;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
KisFilterConfigWidget * KisSobelFilter::createConfigurationWidget(TQWidget* parent, KisPaintDeviceSP)
|
|
|
|
{
|
|
|
|
vKisBoolWidgetParam param;
|
|
|
|
param.push_back( KisBoolWidgetParam( true, i18n("Sobel horizontally"), "doHorizontally" ) );
|
|
|
|
param.push_back( KisBoolWidgetParam( true, i18n("Sobel vertically"), "doVertically" ) );
|
|
|
|
param.push_back( KisBoolWidgetParam( true, i18n("Keep sign of result"), "keepSign" ) );
|
|
|
|
param.push_back( KisBoolWidgetParam( true, i18n("Make image opaque"), "makeOpaque" ) );
|
|
|
|
return new KisMultiBoolFilterWidget(parent, id().id().ascii(), id().id().ascii(), param );
|
|
|
|
}
|
|
|
|
|
|
|
|
KisFilterConfiguration* KisSobelFilter::configuration(TQWidget* nwidget)
|
|
|
|
{
|
|
|
|
KisMultiBoolFilterWidget* widget = (KisMultiBoolFilterWidget*) nwidget;
|
|
|
|
if( widget == 0 )
|
|
|
|
{
|
|
|
|
return new KisSobelFilterConfiguration( true, true, true, true);
|
|
|
|
} else {
|
|
|
|
return new KisSobelFilterConfiguration( widget->valueAt( 0 ), widget->valueAt( 1 ), widget->valueAt( 2 ), widget->valueAt( 3 ) );
|
|
|
|
}
|
|
|
|
}
|