diff options
Diffstat (limited to 'chalk/plugins/filters/bumpmap/bumpmap.cpp')
-rw-r--r-- | chalk/plugins/filters/bumpmap/bumpmap.cpp | 533 |
1 files changed, 533 insertions, 0 deletions
diff --git a/chalk/plugins/filters/bumpmap/bumpmap.cpp b/chalk/plugins/filters/bumpmap/bumpmap.cpp new file mode 100644 index 000000000..9d7712228 --- /dev/null +++ b/chalk/plugins/filters/bumpmap/bumpmap.cpp @@ -0,0 +1,533 @@ +/* + * This file is part of the KDE project + * + * Copyright (c) 2005 Boudewijn <boud@valdyas.org> + * Copyright (c) 2007 Benjamin Schleimer <bensch128@yahoo.com> + * + * 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. + * + * + * This implementation completely and utterly based on the gimp's bumpmap.c, + * copyright: + * Copyright (C) 1997 Federico Mena Quintero <federico@nuclecu.unam.mx> + * Copyright (C) 1997-2000 Jens Lautenbacher <jtl@gimp.org> + * Copyright (C) 2000 Sven Neumann <sven@gimp.org> + * + */ + +#include <stdlib.h> +#include <vector> + +#include <tqpoint.h> +#include <tqlayout.h> +#include <tqcombobox.h> +#include <tqcheckbox.h> +#include <tqbuttongroup.h> +#include <tqradiobutton.h> +#include <tqstring.h> +#include <tqpushbutton.h> +#include <tqlineedit.h> + +#include <knuminput.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 <kcombobox.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_layer.h> +#include <kis_paint_layer.h> +#include <kis_group_layer.h> +#include <kis_adjustment_layer.h> + +#include "wdgbumpmap.h" +#include "bumpmap.h" + +#define MOD(x, y) \ + ((x) < 0 ? ((y) - 1 - ((y) - 1 - (x)) % (y)) : (x) % (y)) + +typedef KGenericFactory<ChalkBumpmap> ChalkBumpmapFactory; +K_EXPORT_COMPONENT_FACTORY( chalkbumpmap, ChalkBumpmapFactory( "chalk" ) ) + +ChalkBumpmap::ChalkBumpmap(TQObject *parent, const char *name, const TQStringList &) + : KParts::Plugin(parent, name) +{ + setInstance(ChalkBumpmapFactory::instance()); + + + if (parent->inherits("KisFilterRegistry")) { + KisFilterRegistry * manager = dynamic_cast<KisFilterRegistry *>(parent); + manager->add(new KisFilterBumpmap()); + } +} + +ChalkBumpmap::~ChalkBumpmap() +{ +} + +KisFilterBumpmap::KisFilterBumpmap() : KisFilter(id(), "map", i18n("&Bumpmap...")) +{ +} + +namespace { + void convertRow(KisPaintDevice * orig, TQ_UINT8 * row, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_UINT8 * lut, TQ_INT32 waterlevel) + { + KisColorSpace * csOrig = orig->colorSpace(); + + KisHLineIteratorPixel origIt = orig->createHLineIterator(x, y, w, false); + for (int i = 0; i < w; ++i) { + row[0] = csOrig->intensity8(origIt.rawData()); + row[0] = lut[waterlevel + ((row[0] - waterlevel) * csOrig->getAlpha(origIt.rawData())) / 255]; + + ++row; + ++origIt; + } + } + +} + +void KisFilterBumpmap::process(KisPaintDeviceSP src, KisPaintDeviceSP dst, KisFilterConfiguration* cfg, const TQRect& rect) +{ + if (!src) return; + if (!dst) return; + if (!cfg) return; + if (!rect.isValid()) return; + if (rect.isNull()) return; + if (rect.isEmpty()) return; + + KisBumpmapConfiguration * config = (KisBumpmapConfiguration*)cfg; + + TQ_INT32 xofs, yofs; /// The x,y offset values + TQ_INT32 lx, ly; /* X and Y components of light vector */ + TQ_INT32 nz2, nzlz; /* nz^2, nz*lz */ + TQ_INT32 background; /* Shade for vertical normals */ + double compensation; /* Background compensation */ + TQ_UINT8 lut[256]; /* Look-up table for modes */ + + double azimuth; + double elevation; + TQ_INT32 lz, nz; + TQ_INT32 i; + double n; + + // ------------------ Prepare parameters + + /* Convert the offsets */ + xofs = -config->xofs; + yofs = -config->yofs; + + /* Convert to radians */ + azimuth = M_PI * config->azimuth / 180.0; + elevation = M_PI * config->elevation / 180.0; + + /* Calculate the light vector */ + lx = (TQ_INT32)(cos(azimuth) * cos(elevation) * 255.0); + ly = (TQ_INT32)(sin(azimuth) * cos(elevation) * 255.0); + + lz = (TQ_INT32)(sin(elevation) * 255.0); + + /* Calculate constant Z component of surface normal */ + nz = (TQ_INT32)((6 * 255) / config->depth); + nz2 = nz * nz; + nzlz = nz * lz; + + /* Optimize for vertical normals */ + background = lz; + + /* Calculate darkness compensation factor */ + compensation = sin(elevation); + + /* Create look-up table for map type */ + for (i = 0; i < 256; i++) + { + switch (config->type) + { + case SPHERICAL: + n = i / 255.0 - 1.0; + lut[i] = (int) (255.0 * sqrt(1.0 - n * n) + 0.5); + break; + + case SINUSOIDAL: + n = i / 255.0; + lut[i] = (int) (255.0 * + (sin((-M_PI / 2.0) + M_PI * n) + 1.0) / + 2.0 + 0.5); + break; + + case LINEAR: + default: + lut[i] = i; + } + + if (config->invert) + lut[i] = 255 - lut[i]; + } + + + // Crate a grayscale layer from the bumpmap layer. + TQRect bmRect; + KisPaintDevice * bumpmap; + + if (!config->bumpmap.isNull() && src->image()) { + KisLayerSP l = src->image()->findLayer(config->bumpmap); + KisPaintDeviceSP bumplayer = 0; + + KisPaintLayer * pl = dynamic_cast<KisPaintLayer*>(l.data()); + if (pl) { + bumplayer = pl->paintDevice(); + } + else { + KisGroupLayer * gl = dynamic_cast<KisGroupLayer*>(l.data()); + if (gl) { + bumplayer = gl->projection(gl->extent()); + } + else { + KisAdjustmentLayer * al = dynamic_cast<KisAdjustmentLayer*>(l.data()); + if (al) { + bumplayer = al->cachedPaintDevice(); + } + } + } + + + if (bumplayer) { + bmRect = bumplayer->exactBounds(); + bumpmap = bumplayer.data(); + } + else { + bmRect = rect; + bumpmap = src; + } + } + + if(!bmRect.isValid()) { + bmRect = rect; + bumpmap = src; + } + + kdDebug(12345) << "KisFilterBumpmap::process: rect=" << rect << ", bumpmap rect=" << bmRect << "\n"; + + setProgressTotalSteps(rect.height()); + + // ---------------------- Load initial three bumpmap scanlines + + KisColorSpace * srcCs = src->colorSpace(); + TQValueVector<KisChannelInfo *> channels = srcCs->channels(); + + // One byte per pixel, converted from the bumpmap layer. + TQ_UINT8 * bm_row1 = new TQ_UINT8[bmRect.width()]; + TQ_UINT8 * bm_row2 = new TQ_UINT8[bmRect.width()]; + TQ_UINT8 * bm_row3 = new TQ_UINT8[bmRect.width()]; + TQ_UINT8 * tmp_row; + + // ------------------- Map the bumps + TQ_INT32 yofs1, yofs2, yofs3; + + // ------------------- Initialize offsets + if (config->tiled) { + yofs2 = MOD (yofs, bmRect.height()); + yofs1 = MOD (yofs2 - 1, bmRect.height()); + yofs3 = MOD (yofs2 + 1, bmRect.height()); + } + else { + yofs2 = 0; + yofs1 = yofs2 - 1; + yofs3 = yofs2 + 1; + } + convertRow(bumpmap, bm_row1, bmRect.x(), yofs1+bmRect.top(), bmRect.width(), lut, config->waterlevel); + convertRow(bumpmap, bm_row2, bmRect.x(), yofs2+bmRect.top(), bmRect.width(), lut, config->waterlevel); + convertRow(bumpmap, bm_row3, bmRect.x(), yofs3+bmRect.top(), bmRect.width(), lut, config->waterlevel); + + for (int y = rect.top(); y<=rect.bottom(); y++) { + const TQ_INT32 yBump = y+yofs; + if(config->tiled || (bmRect.top()<=yBump && yBump<=bmRect.bottom()) ) { + // Get the iterators + KisHLineIteratorPixel dstIt = dst->createHLineIterator(rect.x(), y, rect.width(), true); + KisHLineIteratorPixel srcIt = src->createHLineIterator(rect.x(), y, rect.width(), false); + + //while (x < sel_w || cancelRequested()) { + while (!srcIt.isDone() && !cancelRequested()) { + if (srcIt.isSelected()) { + + const TQ_INT32 xBump = srcIt.x()+xofs; + TQ_INT32 nx, ny; + // Calculate surface normal from bumpmap + if (config->tiled || bmRect.left() <= xBump && xBump <= bmRect.right()) { + + TQ_INT32 xofs1, xofs2, xofs3; + if (config->tiled) { + xofs2 = MOD (xBump-bmRect.left(), bmRect.width()); + xofs1 = MOD (xofs2 - 1, bmRect.width()); + xofs3 = MOD (xofs2 + 1, bmRect.width()); + } else { + xofs2 = MOD (xBump-bmRect.left(), bmRect.width()); + xofs1 = ::max (xofs2 - 1, 0); + xofs3 = ::min (xofs2 + 1, bmRect.width()); + } + + nx = (bm_row1[xofs1] + bm_row2[xofs1] + bm_row3[xofs1] - + bm_row1[xofs3] - bm_row2[xofs3] - bm_row3[xofs3]); + ny = (bm_row3[xofs1] + bm_row3[xofs2] + bm_row3[xofs3] - + bm_row1[xofs1] - bm_row1[xofs2] - bm_row1[xofs3]); + } else { + nx = 0; + ny = 0; + } + + // Shade + TQ_INT32 shade; + if ((nx == 0) && (ny == 0)) { + shade = background; + } else { + TQ_INT32 ndotl = (nx * lx) + (ny * ly) + nzlz; + + if (ndotl < 0) { + shade = (TQ_INT32)(compensation * config->ambient); + } else { + shade = (TQ_INT32)(ndotl / sqrt(nx * nx + ny * ny + nz2)); + shade = (TQ_INT32)(shade + TQMAX(0, (255 * compensation - shade)) * config->ambient / 255); + } + } + + // Paint + srcCs->darken(srcIt.rawData(), dstIt.rawData(), shade, config->compensate, compensation, 1); + } + + ++srcIt; + ++dstIt; + } + + // Go to the next row + tmp_row = bm_row1; + bm_row1 = bm_row2; + bm_row2 = bm_row3; + bm_row3 = tmp_row; + + yofs2++; + if (yofs2 >= bmRect.height()) { yofs2 = 0; } + + if (config->tiled) { + yofs3 = MOD (yofs2 + 1, bmRect.height()); + } else { + yofs3 = yofs2 + 1; + } + convertRow(bumpmap, bm_row3, bmRect.x(), yofs3+bmRect.top(), bmRect.width(), lut, config->waterlevel); + } + + incProgress(); + } + + delete [] bm_row1; + delete [] bm_row2; + delete [] bm_row3; + setProgressDone(); + +} + +KisFilterConfigWidget * KisFilterBumpmap::createConfigurationWidget(TQWidget* parent, KisPaintDeviceSP dev) +{ + KisBumpmapConfigWidget * w = new KisBumpmapConfigWidget(this, dev, parent); + + + return w; +} + +KisFilterConfiguration * KisFilterBumpmap::configuration(TQWidget * w) +{ + + KisBumpmapConfigWidget * widget = dynamic_cast<KisBumpmapConfigWidget *>(w); + if (widget == 0) { + return new KisBumpmapConfiguration(); + } + else { + return widget->config(); + } +} + +KisFilterConfiguration * KisFilterBumpmap::configuration() +{ + return new KisBumpmapConfiguration(); +} + + +KisBumpmapConfiguration::KisBumpmapConfiguration() + : KisFilterConfiguration( "bumpmap", 1 ) +{ + bumpmap = TQString(); + azimuth = 135.0; + elevation = 45.0; + depth = 3; + xofs = 0; + yofs = 0; + waterlevel = 0; + ambient = 0; + compensate = true; + invert = false; + tiled = true; + type = chalk::LINEAR; +} +void KisBumpmapConfiguration::fromXML(const TQString & s) +{ + KisFilterConfiguration::fromXML( s ); + + bumpmap = TQString(); + azimuth = 135.0; + elevation = 45.0; + depth = 3; + xofs = 0; + yofs = 0; + waterlevel = 0; + ambient = 0; + compensate = true; + invert = false; + tiled = true; + type = chalk::LINEAR; + + TQVariant v; + + v = getProperty("bumpmap"); + if (v.isValid()) { bumpmap = v.asString(); } + v = getProperty("azimuth"); + if (v.isValid()) { azimuth = v.asDouble(); } + v = getProperty("elevation"); + if (v.isValid()) { elevation = v.asDouble();} + v = getProperty("depth"); + if (v.isValid()) { depth = v.asDouble(); } + v = getProperty("xofs"); + if (v.isValid()) { xofs = v.asInt(); } + v = getProperty("yofs"); + if (v.isValid()) { yofs = v.asInt();} + v = getProperty("waterlevel"); + if (v.isValid()) { waterlevel = v.asInt();} + v = getProperty("ambient"); + if (v.isValid()) { ambient = v.asInt();} + v = getProperty("compensate"); + if (v.isValid()) { compensate = v.asBool(); } + v = getProperty("invert"); + if (v.isValid()) { invert = v.asBool(); } + v = getProperty("tiled"); + if (v.isValid()) { tiled = v.asBool();} + v = getProperty("type"); + if (v.isValid()) { type = (enumBumpmapType)v.asInt(); } + +} + + +TQString KisBumpmapConfiguration::toString() +{ + m_properties.clear(); + + //setProperty("bumpmap", TQVariant(bumpmap)); + setProperty("azimuth", TQVariant(azimuth)); + setProperty("elevation", TQVariant(elevation)); + setProperty("depth", TQVariant(depth)); + setProperty("xofs", TQVariant(xofs)); + setProperty("yofs", TQVariant(yofs)); + setProperty("waterlevel", TQVariant(waterlevel)); + setProperty("ambient", TQVariant(ambient)); + setProperty("compensate", TQVariant(compensate)); + setProperty("invert", TQVariant(invert)); + setProperty("tiled", TQVariant(tiled)); + setProperty("type", TQVariant(type)); + + return KisFilterConfiguration::toString(); +} + +KisBumpmapConfigWidget::KisBumpmapConfigWidget(KisFilter *, KisPaintDeviceSP dev, TQWidget * parent, const char * name, WFlags f) + : KisFilterConfigWidget(parent, name, f) +{ + m_page = new WdgBumpmap(this); + TQHBoxLayout * l = new TQHBoxLayout(this); + TQ_CHECK_PTR(l); + + l->add(m_page); + + // Find all of the layers in the group + if(dev->image() ) { + KisGroupLayerSP root = dev->image()->rootLayer(); + for(KisLayerSP layer = root->firstChild(); layer; layer = layer->nextSibling()) + { + m_page->cboxSourceLayer->insertItem(layer->name()); + } + } + + // Connect all of the widgets to update signal + connect( m_page->radioLinear, TQT_SIGNAL( toggled(bool)), TQT_SIGNAL(sigPleaseUpdatePreview())); + connect( m_page->radioSpherical, TQT_SIGNAL( toggled(bool)), TQT_SIGNAL(sigPleaseUpdatePreview())); + connect( m_page->radioSinusoidal, TQT_SIGNAL( toggled(bool)), TQT_SIGNAL(sigPleaseUpdatePreview())); + connect( m_page->chkCompensate, TQT_SIGNAL( toggled(bool)), TQT_SIGNAL(sigPleaseUpdatePreview())); + connect( m_page->chkInvert, TQT_SIGNAL( toggled(bool)), TQT_SIGNAL(sigPleaseUpdatePreview())); + connect( m_page->chkTiled, TQT_SIGNAL( toggled(bool)), TQT_SIGNAL(sigPleaseUpdatePreview())); + connect( m_page->dblAzimuth, TQT_SIGNAL( valueChanged(int)), TQT_SIGNAL(sigPleaseUpdatePreview())); + connect( m_page->dblElevation, TQT_SIGNAL( valueChanged(int)), TQT_SIGNAL(sigPleaseUpdatePreview())); + connect( m_page->dblDepth, TQT_SIGNAL( valueChanged(int)), TQT_SIGNAL(sigPleaseUpdatePreview())); + connect( m_page->intXOffset, TQT_SIGNAL( valueChanged(int)), TQT_SIGNAL(sigPleaseUpdatePreview())); + connect( m_page->intYOffset, TQT_SIGNAL( valueChanged(int)), TQT_SIGNAL(sigPleaseUpdatePreview())); + connect( m_page->intWaterLevel, TQT_SIGNAL( valueChanged(int)), TQT_SIGNAL(sigPleaseUpdatePreview())); + connect( m_page->intAmbient, TQT_SIGNAL( valueChanged(int)), TQT_SIGNAL(sigPleaseUpdatePreview())); +} + +KisBumpmapConfiguration * KisBumpmapConfigWidget::config() +{ + KisBumpmapConfiguration * cfg = new KisBumpmapConfiguration(); + cfg->bumpmap = m_page->cboxSourceLayer->currentText(); + cfg->azimuth = m_page->dblAzimuth->value(); + cfg->elevation = m_page->dblElevation->value(); + cfg->depth = m_page->dblDepth->value(); + cfg->xofs = m_page->intXOffset->value(); + cfg->yofs = m_page->intYOffset->value(); + cfg->waterlevel = m_page->intWaterLevel->value(); + cfg->ambient = m_page->intAmbient->value(); + cfg->compensate = m_page->chkCompensate->isChecked(); + cfg->invert = m_page->chkInvert->isChecked(); + cfg->tiled = m_page->chkTiled->isChecked(); + cfg->type = (enumBumpmapType)m_page->grpType->selectedId(); + + return cfg; +} + +void KisBumpmapConfigWidget::setConfiguration(KisFilterConfiguration * config) +{ + KisBumpmapConfiguration * cfg = dynamic_cast<KisBumpmapConfiguration*>(config); + if (!cfg) return; + + // NOTE: maybe we should find the item instead? + m_page->cboxSourceLayer->setCurrentText( cfg->bumpmap ); + m_page->dblAzimuth->setValue(cfg->azimuth); + m_page->dblElevation->setValue(cfg->elevation); + m_page->dblDepth->setValue(cfg->depth); + m_page->intXOffset->setValue(cfg->xofs); + m_page->intYOffset->setValue(cfg->yofs); + m_page->intWaterLevel->setValue(cfg->waterlevel); + m_page->intAmbient->setValue(cfg->ambient); + m_page->chkCompensate->setChecked(cfg->compensate); + m_page->chkInvert->setChecked(cfg->invert); + m_page->chkTiled->setChecked(cfg->tiled); + m_page->grpType->setButton(cfg->type); + +} + +#include "bumpmap.moc" |