|
|
|
/*
|
|
|
|
* This file is part of Chalk
|
|
|
|
*
|
|
|
|
* Copyright (c) 2004 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
|
|
|
|
*
|
|
|
|
* ported from digikam, Copyright 2004 by Gilles Caulier,
|
|
|
|
* Original Oilpaint algorithm copyrighted 2004 by
|
|
|
|
* Pieter Z. Voloshyn <pieter_voloshyn at ame.com.br>.
|
|
|
|
*
|
|
|
|
* 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_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<KisFilterConfiguration*> KisOilPaintFilter::listOfExamplesConfiguration(KisPaintDeviceSP )
|
|
|
|
{
|
|
|
|
std::list<KisFilterConfiguration*> list;
|
|
|
|
list.insert(list.begin(), new KisOilPaintFilterConfiguration( 1, 30));
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|