/* * This file is part of Chalk * * Copyright (c) 2004 Michael Thaler * * ported from digikam, Copyright 2004 by Gilles Caulier, * Original Oilpaint algorithm copyrighted 2004 by * Pieter Z. Voloshyn . * * 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_integer_filter_widget.h" #include "kis_oilpaint_filter.h" KisOilPaintFilter::KisOilPaintFilter() : KisFilter(id(), "artistic", i18n("&Oilpaint...")) { } void KisOilPaintFilter::process(KisPaintDeviceSP src, KisPaintDeviceSP dst, KisFilterConfiguration* configuration, const TQRect& rect) { if (!configuration) { kdWarning() << "No configuration object for oilpaint filter\n"; return; } Q_UNUSED(dst); TQ_INT32 x = rect.x(), y = rect.y(); TQ_INT32 width = rect.width(); TQ_INT32 height = rect.height(); //read the filter configuration values from the KisFilterConfiguration object TQ_UINT32 brushSize = ((KisOilPaintFilterConfiguration*)configuration)->brushSize(); TQ_UINT32 smooth = ((KisOilPaintFilterConfiguration*)configuration)->smooth(); OilPaint(src, dst, x, y, width, height, brushSize, smooth); } // This method have been ported from Pieter Z. Voloshyn algorithm code. /* Function to apply the OilPaint effect. * * data => The image data in RGBA mode. * w => Width of image. * h => Height of image. * BrushSize => Brush size. * Smoothness => Smooth value. * * Theory => Using MostFrequentColor function we take the main color in * a matrix and simply write at the original position. */ void KisOilPaintFilter::OilPaint(KisPaintDeviceSP src, KisPaintDeviceSP dst, int x, int y, int w, int h, int BrushSize, int Smoothness) { setProgressTotalSteps(h); setProgressStage(i18n("Applying oilpaint filter..."),0); TQRect bounds(x, y, w, h); for (TQ_INT32 yOffset = 0; yOffset < h; yOffset++) { KisHLineIteratorPixel it = src->createHLineIterator(x, y + yOffset, w, false); KisHLineIteratorPixel dstIt = dst->createHLineIterator(x, y + yOffset, w, true); while (!it.isDone() && !cancelRequested()) { if (it.isSelected()) { uint color = MostFrequentColor(src, bounds, it.x(), it.y(), BrushSize, Smoothness); dst->colorSpace()->fromTQColor(TQColor(tqRed(color), tqGreen(color), tqBlue(color)), tqAlpha(color), dstIt.rawData()); } ++it; ++dstIt; } setProgress(yOffset); } setProgressDone(); } // This method have been ported from Pieter Z. Voloshyn algorithm code. /* Function to determine the most frequent color in a matrix * * Bits => Bits array * Width => Image width * Height => Image height * X => Position horizontal * Y => Position vertical * Radius => Is the radius of the matrix to be analized * Intensity => Intensity to calcule * * Theory => This function creates a matrix with the analized pixel in * the center of this matrix and find the most frequenty color */ uint KisOilPaintFilter::MostFrequentColor (KisPaintDeviceSP src, const TQRect& bounds, int X, int Y, int Radius, int Intensity) { uint color; uint I; double Scale = Intensity / 255.0; // Alloc some arrays to be used uchar *IntensityCount = new uchar[(Intensity + 1) * sizeof (uchar)]; uint *AverageColorR = new uint[(Intensity + 1) * sizeof (uint)]; uint *AverageColorG = new uint[(Intensity + 1) * sizeof (uint)]; uint *AverageColorB = new uint[(Intensity + 1) * sizeof (uint)]; // Erase the array memset(IntensityCount, 0, (Intensity + 1) * sizeof (uchar)); /*for (i = 0; i <= Intensity; ++i) IntensityCount[i] = 0;*/ KisRectIteratorPixel it = src->createRectIterator(X - Radius, Y - Radius, (2 * Radius) + 1, (2 * Radius) + 1, false); while (!it.isDone()) { if (bounds.contains(it.x(), it.y())) { // XXX: COLORSPACE_INDEPENDENCE TQColor c; src->colorSpace()->toTQColor(it.rawData(), &c); // Swapping red and blue here is done because that gives the same // output as digikam, even though it might be interpreted as a bug // in both applications. int b = c.red(); int g = c.green(); int r = c.blue(); I = (uint)(GetIntensity (r, g, b) * Scale); IntensityCount[I]++; if (IntensityCount[I] == 1) { AverageColorR[I] = r; AverageColorG[I] = g; AverageColorB[I] = b; } else { AverageColorR[I] += r; AverageColorG[I] += g; AverageColorB[I] += b; } } ++it; } I = 0; int MaxInstance = 0; for (int i = 0 ; i <= Intensity ; ++i) { if (IntensityCount[i] > MaxInstance) { I = i; MaxInstance = IntensityCount[i]; } } int R, G, B; if (MaxInstance != 0) { R = AverageColorR[I] / MaxInstance; G = AverageColorG[I] / MaxInstance; B = AverageColorB[I] / MaxInstance; } else { R = 0; G = 0; B = 0; } // Swap red and blue back to get the correct colour. color = tqRgb (B, G, R); delete [] IntensityCount; // free all the arrays delete [] AverageColorR; delete [] AverageColorG; delete [] AverageColorB; return (color); // return the most frequenty color } KisFilterConfigWidget * KisOilPaintFilter::createConfigurationWidget(TQWidget* parent, KisPaintDeviceSP /*dev*/) { vKisIntegerWidgetParam param; param.push_back( KisIntegerWidgetParam( 1, 5, 1, i18n("Brush size"), "brushSize" ) ); param.push_back( KisIntegerWidgetParam( 10, 255, 30, i18n("Smooth"), "smooth" ) ); return new KisMultiIntegerFilterWidget(parent, id().id().ascii(), id().id().ascii(), param ); } KisFilterConfiguration* KisOilPaintFilter::configuration(TQWidget* nwidget) { KisMultiIntegerFilterWidget* widget = (KisMultiIntegerFilterWidget*) nwidget; if( widget == 0 ) { return new KisOilPaintFilterConfiguration( 1, 30); } else { return new KisOilPaintFilterConfiguration( widget->valueAt( 0 ), widget->valueAt( 1 ) ); } } std::list KisOilPaintFilter::listOfExamplesConfiguration(KisPaintDeviceSP ) { std::list list; list.insert(list.begin(), new KisOilPaintFilterConfiguration( 1, 30)); return list; }