summaryrefslogtreecommitdiffstats
path: root/chalk/plugins/filters/bumpmap/bumpmap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'chalk/plugins/filters/bumpmap/bumpmap.cpp')
-rw-r--r--chalk/plugins/filters/bumpmap/bumpmap.cpp533
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"