KOffice – TDE office suite
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

534 lines
17KB

  1. /*
  2. * This file is part of the KDE project
  3. *
  4. * Copyright (c) 2005 Boudewijn <boud@valdyas.org>
  5. * Copyright (c) 2007 Benjamin Schleimer <bensch128@yahoo.com>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  20. *
  21. *
  22. * This implementation completely and utterly based on the gimp's bumpmap.c,
  23. * copyright:
  24. * Copyright (C) 1997 Federico Mena Quintero <federico@nuclecu.unam.mx>
  25. * Copyright (C) 1997-2000 Jens Lautenbacher <jtl@gimp.org>
  26. * Copyright (C) 2000 Sven Neumann <sven@gimp.org>
  27. *
  28. */
  29. #include <stdlib.h>
  30. #include <vector>
  31. #include <tqpoint.h>
  32. #include <tqlayout.h>
  33. #include <tqcombobox.h>
  34. #include <tqcheckbox.h>
  35. #include <tqbuttongroup.h>
  36. #include <tqradiobutton.h>
  37. #include <tqstring.h>
  38. #include <tqpushbutton.h>
  39. #include <tqlineedit.h>
  40. #include <knuminput.h>
  41. #include <tdelocale.h>
  42. #include <kiconloader.h>
  43. #include <kinstance.h>
  44. #include <tdemessagebox.h>
  45. #include <kstandarddirs.h>
  46. #include <tdetempfile.h>
  47. #include <kdebug.h>
  48. #include <kgenericfactory.h>
  49. #include <kcombobox.h>
  50. #include <kis_doc.h>
  51. #include <kis_image.h>
  52. #include <kis_iterators_pixel.h>
  53. #include <kis_layer.h>
  54. #include <kis_filter_registry.h>
  55. #include <kis_global.h>
  56. #include <kis_types.h>
  57. #include <kis_layer.h>
  58. #include <kis_paint_layer.h>
  59. #include <kis_group_layer.h>
  60. #include <kis_adjustment_layer.h>
  61. #include "wdgbumpmap.h"
  62. #include "bumpmap.h"
  63. #define MOD(x, y) \
  64. ((x) < 0 ? ((y) - 1 - ((y) - 1 - (x)) % (y)) : (x) % (y))
  65. typedef KGenericFactory<ChalkBumpmap> ChalkBumpmapFactory;
  66. K_EXPORT_COMPONENT_FACTORY( chalkbumpmap, ChalkBumpmapFactory( "chalk" ) )
  67. ChalkBumpmap::ChalkBumpmap(TQObject *parent, const char *name, const TQStringList &)
  68. : KParts::Plugin(parent, name)
  69. {
  70. setInstance(ChalkBumpmapFactory::instance());
  71. if (parent->inherits("KisFilterRegistry")) {
  72. KisFilterRegistry * manager = dynamic_cast<KisFilterRegistry *>(parent);
  73. manager->add(new KisFilterBumpmap());
  74. }
  75. }
  76. ChalkBumpmap::~ChalkBumpmap()
  77. {
  78. }
  79. KisFilterBumpmap::KisFilterBumpmap() : KisFilter(id(), "map", i18n("&Bumpmap..."))
  80. {
  81. }
  82. namespace {
  83. void convertRow(KisPaintDevice * orig, TQ_UINT8 * row, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_UINT8 * lut, TQ_INT32 waterlevel)
  84. {
  85. KisColorSpace * csOrig = orig->colorSpace();
  86. KisHLineIteratorPixel origIt = orig->createHLineIterator(x, y, w, false);
  87. for (int i = 0; i < w; ++i) {
  88. row[0] = csOrig->intensity8(origIt.rawData());
  89. row[0] = lut[waterlevel + ((row[0] - waterlevel) * csOrig->getAlpha(origIt.rawData())) / 255];
  90. ++row;
  91. ++origIt;
  92. }
  93. }
  94. }
  95. void KisFilterBumpmap::process(KisPaintDeviceSP src, KisPaintDeviceSP dst, KisFilterConfiguration* cfg, const TQRect& rect)
  96. {
  97. if (!src) return;
  98. if (!dst) return;
  99. if (!cfg) return;
  100. if (!rect.isValid()) return;
  101. if (rect.isNull()) return;
  102. if (rect.isEmpty()) return;
  103. KisBumpmapConfiguration * config = (KisBumpmapConfiguration*)cfg;
  104. TQ_INT32 xofs, yofs; /// The x,y offset values
  105. TQ_INT32 lx, ly; /* X and Y components of light vector */
  106. TQ_INT32 nz2, nzlz; /* nz^2, nz*lz */
  107. TQ_INT32 background; /* Shade for vertical normals */
  108. double compensation; /* Background compensation */
  109. TQ_UINT8 lut[256]; /* Look-up table for modes */
  110. double azimuth;
  111. double elevation;
  112. TQ_INT32 lz, nz;
  113. TQ_INT32 i;
  114. double n;
  115. // ------------------ Prepare parameters
  116. /* Convert the offsets */
  117. xofs = -config->xofs;
  118. yofs = -config->yofs;
  119. /* Convert to radians */
  120. azimuth = M_PI * config->azimuth / 180.0;
  121. elevation = M_PI * config->elevation / 180.0;
  122. /* Calculate the light vector */
  123. lx = (TQ_INT32)(cos(azimuth) * cos(elevation) * 255.0);
  124. ly = (TQ_INT32)(sin(azimuth) * cos(elevation) * 255.0);
  125. lz = (TQ_INT32)(sin(elevation) * 255.0);
  126. /* Calculate constant Z component of surface normal */
  127. nz = (TQ_INT32)((6 * 255) / config->depth);
  128. nz2 = nz * nz;
  129. nzlz = nz * lz;
  130. /* Optimize for vertical normals */
  131. background = lz;
  132. /* Calculate darkness compensation factor */
  133. compensation = sin(elevation);
  134. /* Create look-up table for map type */
  135. for (i = 0; i < 256; i++)
  136. {
  137. switch (config->type)
  138. {
  139. case SPHERICAL:
  140. n = i / 255.0 - 1.0;
  141. lut[i] = (int) (255.0 * sqrt(1.0 - n * n) + 0.5);
  142. break;
  143. case SINUSOIDAL:
  144. n = i / 255.0;
  145. lut[i] = (int) (255.0 *
  146. (sin((-M_PI / 2.0) + M_PI * n) + 1.0) /
  147. 2.0 + 0.5);
  148. break;
  149. case LINEAR:
  150. default:
  151. lut[i] = i;
  152. }
  153. if (config->invert)
  154. lut[i] = 255 - lut[i];
  155. }
  156. // Crate a grayscale layer from the bumpmap layer.
  157. TQRect bmRect;
  158. KisPaintDevice * bumpmap;
  159. if (!config->bumpmap.isNull() && src->image()) {
  160. KisLayerSP l = src->image()->findLayer(config->bumpmap);
  161. KisPaintDeviceSP bumplayer = 0;
  162. KisPaintLayer * pl = dynamic_cast<KisPaintLayer*>(l.data());
  163. if (pl) {
  164. bumplayer = pl->paintDevice();
  165. }
  166. else {
  167. KisGroupLayer * gl = dynamic_cast<KisGroupLayer*>(l.data());
  168. if (gl) {
  169. bumplayer = gl->projection(gl->extent());
  170. }
  171. else {
  172. KisAdjustmentLayer * al = dynamic_cast<KisAdjustmentLayer*>(l.data());
  173. if (al) {
  174. bumplayer = al->cachedPaintDevice();
  175. }
  176. }
  177. }
  178. if (bumplayer) {
  179. bmRect = bumplayer->exactBounds();
  180. bumpmap = bumplayer.data();
  181. }
  182. else {
  183. bmRect = rect;
  184. bumpmap = src;
  185. }
  186. }
  187. if(!bmRect.isValid()) {
  188. bmRect = rect;
  189. bumpmap = src;
  190. }
  191. kdDebug(12345) << "KisFilterBumpmap::process: rect=" << rect << ", bumpmap rect=" << bmRect << "\n";
  192. setProgressTotalSteps(rect.height());
  193. // ---------------------- Load initial three bumpmap scanlines
  194. KisColorSpace * srcCs = src->colorSpace();
  195. TQValueVector<KisChannelInfo *> channels = srcCs->channels();
  196. // One byte per pixel, converted from the bumpmap layer.
  197. TQ_UINT8 * bm_row1 = new TQ_UINT8[bmRect.width()];
  198. TQ_UINT8 * bm_row2 = new TQ_UINT8[bmRect.width()];
  199. TQ_UINT8 * bm_row3 = new TQ_UINT8[bmRect.width()];
  200. TQ_UINT8 * tmp_row;
  201. // ------------------- Map the bumps
  202. TQ_INT32 yofs1, yofs2, yofs3;
  203. // ------------------- Initialize offsets
  204. if (config->tiled) {
  205. yofs2 = MOD (yofs, bmRect.height());
  206. yofs1 = MOD (yofs2 - 1, bmRect.height());
  207. yofs3 = MOD (yofs2 + 1, bmRect.height());
  208. }
  209. else {
  210. yofs2 = 0;
  211. yofs1 = yofs2 - 1;
  212. yofs3 = yofs2 + 1;
  213. }
  214. convertRow(bumpmap, bm_row1, bmRect.x(), yofs1+bmRect.top(), bmRect.width(), lut, config->waterlevel);
  215. convertRow(bumpmap, bm_row2, bmRect.x(), yofs2+bmRect.top(), bmRect.width(), lut, config->waterlevel);
  216. convertRow(bumpmap, bm_row3, bmRect.x(), yofs3+bmRect.top(), bmRect.width(), lut, config->waterlevel);
  217. for (int y = rect.top(); y<=rect.bottom(); y++) {
  218. const TQ_INT32 yBump = y+yofs;
  219. if(config->tiled || (bmRect.top()<=yBump && yBump<=bmRect.bottom()) ) {
  220. // Get the iterators
  221. KisHLineIteratorPixel dstIt = dst->createHLineIterator(rect.x(), y, rect.width(), true);
  222. KisHLineIteratorPixel srcIt = src->createHLineIterator(rect.x(), y, rect.width(), false);
  223. //while (x < sel_w || cancelRequested()) {
  224. while (!srcIt.isDone() && !cancelRequested()) {
  225. if (srcIt.isSelected()) {
  226. const TQ_INT32 xBump = srcIt.x()+xofs;
  227. TQ_INT32 nx, ny;
  228. // Calculate surface normal from bumpmap
  229. if (config->tiled || bmRect.left() <= xBump && xBump <= bmRect.right()) {
  230. TQ_INT32 xofs1, xofs2, xofs3;
  231. if (config->tiled) {
  232. xofs2 = MOD (xBump-bmRect.left(), bmRect.width());
  233. xofs1 = MOD (xofs2 - 1, bmRect.width());
  234. xofs3 = MOD (xofs2 + 1, bmRect.width());
  235. } else {
  236. xofs2 = MOD (xBump-bmRect.left(), bmRect.width());
  237. xofs1 = ::max (xofs2 - 1, 0);
  238. xofs3 = ::min (xofs2 + 1, bmRect.width());
  239. }
  240. nx = (bm_row1[xofs1] + bm_row2[xofs1] + bm_row3[xofs1] -
  241. bm_row1[xofs3] - bm_row2[xofs3] - bm_row3[xofs3]);
  242. ny = (bm_row3[xofs1] + bm_row3[xofs2] + bm_row3[xofs3] -
  243. bm_row1[xofs1] - bm_row1[xofs2] - bm_row1[xofs3]);
  244. } else {
  245. nx = 0;
  246. ny = 0;
  247. }
  248. // Shade
  249. TQ_INT32 shade;
  250. if ((nx == 0) && (ny == 0)) {
  251. shade = background;
  252. } else {
  253. TQ_INT32 ndotl = (nx * lx) + (ny * ly) + nzlz;
  254. if (ndotl < 0) {
  255. shade = (TQ_INT32)(compensation * config->ambient);
  256. } else {
  257. shade = (TQ_INT32)(ndotl / sqrt(nx * nx + ny * ny + nz2));
  258. shade = (TQ_INT32)(shade + TQMAX(0, (255 * compensation - shade)) * config->ambient / 255);
  259. }
  260. }
  261. // Paint
  262. srcCs->darken(srcIt.rawData(), dstIt.rawData(), shade, config->compensate, compensation, 1);
  263. }
  264. ++srcIt;
  265. ++dstIt;
  266. }
  267. // Go to the next row
  268. tmp_row = bm_row1;
  269. bm_row1 = bm_row2;
  270. bm_row2 = bm_row3;
  271. bm_row3 = tmp_row;
  272. yofs2++;
  273. if (yofs2 >= bmRect.height()) { yofs2 = 0; }
  274. if (config->tiled) {
  275. yofs3 = MOD (yofs2 + 1, bmRect.height());
  276. } else {
  277. yofs3 = yofs2 + 1;
  278. }
  279. convertRow(bumpmap, bm_row3, bmRect.x(), yofs3+bmRect.top(), bmRect.width(), lut, config->waterlevel);
  280. }
  281. incProgress();
  282. }
  283. delete [] bm_row1;
  284. delete [] bm_row2;
  285. delete [] bm_row3;
  286. setProgressDone();
  287. }
  288. KisFilterConfigWidget * KisFilterBumpmap::createConfigurationWidget(TQWidget* parent, KisPaintDeviceSP dev)
  289. {
  290. KisBumpmapConfigWidget * w = new KisBumpmapConfigWidget(this, dev, parent);
  291. return w;
  292. }
  293. KisFilterConfiguration * KisFilterBumpmap::configuration(TQWidget * w)
  294. {
  295. KisBumpmapConfigWidget * widget = dynamic_cast<KisBumpmapConfigWidget *>(w);
  296. if (widget == 0) {
  297. return new KisBumpmapConfiguration();
  298. }
  299. else {
  300. return widget->config();
  301. }
  302. }
  303. KisFilterConfiguration * KisFilterBumpmap::configuration()
  304. {
  305. return new KisBumpmapConfiguration();
  306. }
  307. KisBumpmapConfiguration::KisBumpmapConfiguration()
  308. : KisFilterConfiguration( "bumpmap", 1 )
  309. {
  310. bumpmap = TQString();
  311. azimuth = 135.0;
  312. elevation = 45.0;
  313. depth = 3;
  314. xofs = 0;
  315. yofs = 0;
  316. waterlevel = 0;
  317. ambient = 0;
  318. compensate = true;
  319. invert = false;
  320. tiled = true;
  321. type = chalk::LINEAR;
  322. }
  323. void KisBumpmapConfiguration::fromXML(const TQString & s)
  324. {
  325. KisFilterConfiguration::fromXML( s );
  326. bumpmap = TQString();
  327. azimuth = 135.0;
  328. elevation = 45.0;
  329. depth = 3;
  330. xofs = 0;
  331. yofs = 0;
  332. waterlevel = 0;
  333. ambient = 0;
  334. compensate = true;
  335. invert = false;
  336. tiled = true;
  337. type = chalk::LINEAR;
  338. TQVariant v;
  339. v = getProperty("bumpmap");
  340. if (v.isValid()) { bumpmap = v.asString(); }
  341. v = getProperty("azimuth");
  342. if (v.isValid()) { azimuth = v.asDouble(); }
  343. v = getProperty("elevation");
  344. if (v.isValid()) { elevation = v.asDouble();}
  345. v = getProperty("depth");
  346. if (v.isValid()) { depth = v.asDouble(); }
  347. v = getProperty("xofs");
  348. if (v.isValid()) { xofs = v.asInt(); }
  349. v = getProperty("yofs");
  350. if (v.isValid()) { yofs = v.asInt();}
  351. v = getProperty("waterlevel");
  352. if (v.isValid()) { waterlevel = v.asInt();}
  353. v = getProperty("ambient");
  354. if (v.isValid()) { ambient = v.asInt();}
  355. v = getProperty("compensate");
  356. if (v.isValid()) { compensate = v.asBool(); }
  357. v = getProperty("invert");
  358. if (v.isValid()) { invert = v.asBool(); }
  359. v = getProperty("tiled");
  360. if (v.isValid()) { tiled = v.asBool();}
  361. v = getProperty("type");
  362. if (v.isValid()) { type = (enumBumpmapType)v.asInt(); }
  363. }
  364. TQString KisBumpmapConfiguration::toString()
  365. {
  366. m_properties.clear();
  367. //setProperty("bumpmap", TQVariant(bumpmap));
  368. setProperty("azimuth", TQVariant(azimuth));
  369. setProperty("elevation", TQVariant(elevation));
  370. setProperty("depth", TQVariant(depth));
  371. setProperty("xofs", TQVariant(xofs));
  372. setProperty("yofs", TQVariant(yofs));
  373. setProperty("waterlevel", TQVariant(waterlevel));
  374. setProperty("ambient", TQVariant(ambient));
  375. setProperty("compensate", TQVariant(compensate));
  376. setProperty("invert", TQVariant(invert));
  377. setProperty("tiled", TQVariant(tiled));
  378. setProperty("type", TQVariant(type));
  379. return KisFilterConfiguration::toString();
  380. }
  381. KisBumpmapConfigWidget::KisBumpmapConfigWidget(KisFilter *, KisPaintDeviceSP dev, TQWidget * parent, const char * name, WFlags f)
  382. : KisFilterConfigWidget(parent, name, f)
  383. {
  384. m_page = new WdgBumpmap(this);
  385. TQHBoxLayout * l = new TQHBoxLayout(this);
  386. TQ_CHECK_PTR(l);
  387. l->add(m_page);
  388. // Find all of the layers in the group
  389. if(dev->image() ) {
  390. KisGroupLayerSP root = dev->image()->rootLayer();
  391. for(KisLayerSP layer = root->firstChild(); layer; layer = layer->nextSibling())
  392. {
  393. m_page->cboxSourceLayer->insertItem(layer->name());
  394. }
  395. }
  396. // Connect all of the widgets to update signal
  397. connect( m_page->radioLinear, TQT_SIGNAL( toggled(bool)), TQT_SIGNAL(sigPleaseUpdatePreview()));
  398. connect( m_page->radioSpherical, TQT_SIGNAL( toggled(bool)), TQT_SIGNAL(sigPleaseUpdatePreview()));
  399. connect( m_page->radioSinusoidal, TQT_SIGNAL( toggled(bool)), TQT_SIGNAL(sigPleaseUpdatePreview()));
  400. connect( m_page->chkCompensate, TQT_SIGNAL( toggled(bool)), TQT_SIGNAL(sigPleaseUpdatePreview()));
  401. connect( m_page->chkInvert, TQT_SIGNAL( toggled(bool)), TQT_SIGNAL(sigPleaseUpdatePreview()));
  402. connect( m_page->chkTiled, TQT_SIGNAL( toggled(bool)), TQT_SIGNAL(sigPleaseUpdatePreview()));
  403. connect( m_page->dblAzimuth, TQT_SIGNAL( valueChanged(int)), TQT_SIGNAL(sigPleaseUpdatePreview()));
  404. connect( m_page->dblElevation, TQT_SIGNAL( valueChanged(int)), TQT_SIGNAL(sigPleaseUpdatePreview()));
  405. connect( m_page->dblDepth, TQT_SIGNAL( valueChanged(int)), TQT_SIGNAL(sigPleaseUpdatePreview()));
  406. connect( m_page->intXOffset, TQT_SIGNAL( valueChanged(int)), TQT_SIGNAL(sigPleaseUpdatePreview()));
  407. connect( m_page->intYOffset, TQT_SIGNAL( valueChanged(int)), TQT_SIGNAL(sigPleaseUpdatePreview()));
  408. connect( m_page->intWaterLevel, TQT_SIGNAL( valueChanged(int)), TQT_SIGNAL(sigPleaseUpdatePreview()));
  409. connect( m_page->intAmbient, TQT_SIGNAL( valueChanged(int)), TQT_SIGNAL(sigPleaseUpdatePreview()));
  410. }
  411. KisBumpmapConfiguration * KisBumpmapConfigWidget::config()
  412. {
  413. KisBumpmapConfiguration * cfg = new KisBumpmapConfiguration();
  414. cfg->bumpmap = m_page->cboxSourceLayer->currentText();
  415. cfg->azimuth = m_page->dblAzimuth->value();
  416. cfg->elevation = m_page->dblElevation->value();
  417. cfg->depth = m_page->dblDepth->value();
  418. cfg->xofs = m_page->intXOffset->value();
  419. cfg->yofs = m_page->intYOffset->value();
  420. cfg->waterlevel = m_page->intWaterLevel->value();
  421. cfg->ambient = m_page->intAmbient->value();
  422. cfg->compensate = m_page->chkCompensate->isChecked();
  423. cfg->invert = m_page->chkInvert->isChecked();
  424. cfg->tiled = m_page->chkTiled->isChecked();
  425. cfg->type = (enumBumpmapType)m_page->grpType->selectedId();
  426. return cfg;
  427. }
  428. void KisBumpmapConfigWidget::setConfiguration(KisFilterConfiguration * config)
  429. {
  430. KisBumpmapConfiguration * cfg = dynamic_cast<KisBumpmapConfiguration*>(config);
  431. if (!cfg) return;
  432. // NOTE: maybe we should find the item instead?
  433. m_page->cboxSourceLayer->setCurrentText( cfg->bumpmap );
  434. m_page->dblAzimuth->setValue(cfg->azimuth);
  435. m_page->dblElevation->setValue(cfg->elevation);
  436. m_page->dblDepth->setValue(cfg->depth);
  437. m_page->intXOffset->setValue(cfg->xofs);
  438. m_page->intYOffset->setValue(cfg->yofs);
  439. m_page->intWaterLevel->setValue(cfg->waterlevel);
  440. m_page->intAmbient->setValue(cfg->ambient);
  441. m_page->chkCompensate->setChecked(cfg->compensate);
  442. m_page->chkInvert->setChecked(cfg->invert);
  443. m_page->chkTiled->setChecked(cfg->tiled);
  444. m_page->grpType->setButton(cfg->type);
  445. }
  446. #include "bumpmap.moc"