/* * This file is part of Chalk * * Copyright (c) 2005 Michael Thaler * * ported from Gimp, Copyright (C) 1997 Eiichi Takamori * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 ) ); } }