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.

425 lines
16KB

  1. /*
  2. * This file is part of the KDE project
  3. *
  4. * Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. */
  19. #include <stdlib.h>
  20. #include <vector>
  21. #include <tdelocale.h>
  22. #include <kdebug.h>
  23. #include <kis_iterators_pixel.h>
  24. #include <kis_filter_registry.h>
  25. #include <kis_debug_areas.h>
  26. #include <kis_types.h>
  27. #include <kis_paint_device.h>
  28. #include <kis_debug_areas.h>
  29. #include "wetphysicsfilter.h"
  30. /*
  31. * [11:14] <boud> CyrilleB: I think I know why watercolor drying creates that funny pattern (you can see it if you have a very wet canvas with lots of paint and leave it drying for a while): our dry filter must have an off-by-one error to the right and bottom, which is also why the buggy drying didn't remove all of previously applied paint but left a fringe.
  32. * [11:14] <pippin> does the drying behave kind of like an error diffusion?
  33. * [11:14] <pippin> (it sounds like error diffusion artifacts,.)
  34. * [11:15] <boud> pippin: not sure what error diffusion is...
  35. * [11:15] <pippin> used for digital halftoning
  36. * [11:15] <pippin> take a greyscale image,.. you want to end up with binary (could be less, but let's use 1bit result)
  37. * [11:15] <CyrilleB> boud: the funny pattern is also in wetdreams when you disable wetness visualisation
  38. * [11:15] <boud> CyrilleB: I don't mean the checkerboard pattern
  39. * [11:16] <pippin> then for each pixel you calculate the difference between the current value and the desired value (0 or 255)
  40. * [11:16] <CyrilleB> boud: which one then ?
  41. * [11:16] <pippin> the error is distributed to the neighbour pixels (to the right, down and down to the left in pixels which have not yet been processed
  42. * [11:16] <pippin> )
  43. * [11:16] <boud> CyrilleB: it's only apparent when you let something dry for some time, it looks like meandering snakes (like the old game "snake")
  44. * [11:16] <CyrilleB> pippin: somehow yes
  45. * [11:16] <boud> pippin: that is possible
  46. * [11:17] <pippin> boud: this leads to "bleeding" of data to the right and down,..
  47. * [11:17] <boud> pippin: but on the other hand, when the filter worked on the old tiles (empty ones) it also left a fringe of color.
  48. * [11:17] <pippin> having the "error" spread in different directions on each iteration might fix something like this,.
  49. * [11:18] <boud> Which leads me to think it's an off-by one.
  50. * [11:25] <boud> No, it isn't off by one. Then pippin must be right.
  51. * [11:26] <pippin> if I am, this is a fun debug session, not even having the code or the visual results available,. just hanging around on irc :)
  52. * [11:27] <boud> Well, I don't have time to investigate right now, but it sounds very plausible.
  53. * [11:27] <CyrilleB> pippin: :)
  54. * [11:28] <boud> of course, the code _is_ available :-)
  55. * [11:28] <pippin> if there is some form of diffusion matrix that is directional around the current pixel,. having that mask rotate depending on the modulus of the current iteration # should cancel such an effect out
  56. */
  57. WetPhysicsFilter::WetPhysicsFilter()
  58. : KisFilter(id(), "artistic", i18n("Dry the Paint"))
  59. {
  60. m_adsorbCount = 0;
  61. }
  62. void WetPhysicsFilter::process(KisPaintDeviceSP src, KisPaintDeviceSP dst, KisFilterConfiguration* /*config*/, const TQRect& rect)
  63. {
  64. kdDebug() << "Physics processing " << src->name() << m_adsorbCount << endl;
  65. // XXX: It would be nice be able to interleave this, instead of
  66. // having the same loop over our pixels three times.
  67. flow(src, dst, rect);
  68. if (m_adsorbCount++ == 2) {
  69. // XXX I think we could combine dry and adsorb, yes
  70. adsorb(src, dst, rect);
  71. dry(src, dst, rect);
  72. m_adsorbCount = 0;
  73. }
  74. setProgressDone(); // Must be called even if you don't really support progression
  75. }
  76. void WetPhysicsFilter::flow(KisPaintDeviceSP src, KisPaintDeviceSP /*dst*/, const TQRect & r)
  77. {
  78. /* XXX: Is this like a convolution operation? BSAR */
  79. int width = r.width();
  80. int height = r.height();
  81. kdDebug() << "Flowing: " << r << endl;
  82. /* width of a line in a layer in pixel units, not in bytes -- used to move to the next
  83. line in the fluid masks below */
  84. int rs = width; // rowstride
  85. double * flow_t = new double[width * height];
  86. TQ_CHECK_PTR(flow_t);
  87. double * flow_b = new double[width * height];
  88. TQ_CHECK_PTR(flow_b);
  89. double * flow_l = new double[width * height];
  90. TQ_CHECK_PTR(flow_l);
  91. double * flow_r = new double[width * height];
  92. TQ_CHECK_PTR(flow_r);
  93. double * fluid = new double[width * height];
  94. TQ_CHECK_PTR(fluid);
  95. double * outflow = new double[width * height];
  96. TQ_CHECK_PTR(outflow);
  97. // Height of the paper surface. Do we also increase height because of paint deposits?
  98. int my_height;
  99. // Flow to the top, bottom, left, right of the currentpixel
  100. double ft, fb, fl, fr;
  101. // Temporary pixel constructs
  102. WetPixDbl wet_mix, wet_tmp;
  103. // XXX If the flow touches areas that have not been initialized with a height field yet,
  104. // create a height field.
  105. // We need three iterators, because we're working on a five-point convolution kernel (no corner pixels are being used)
  106. // First iteration: compute fluid deposits around the paper.
  107. TQ_INT32 dx, dy;
  108. dx = r.x();
  109. dy = r.y();
  110. int ix = width + 1; // keeps track where we are in the one-dimensional arrays
  111. for (TQ_INT32 y2 = 1; y2 < height - 1; ++y2) {
  112. KisHLineIteratorPixel srcIt = src->createHLineIterator(dx, dy + y2, width, false);
  113. KisHLineIteratorPixel upIt = src->createHLineIterator(dx + 1, dy + y2 - 1, width - 2, false);
  114. KisHLineIteratorPixel downIt = src->createHLineIterator(dx + 1, dy + y2 + 1, width - 2, false);
  115. // .paint is the first field in our wetpack, so this is ok (even though not nice)
  116. WetPix left = *(reinterpret_cast<WetPix*>(srcIt.rawData()));
  117. ++srcIt;
  118. WetPix current = *(reinterpret_cast<WetPix*>(srcIt.rawData()));
  119. ++srcIt;
  120. WetPix right = *(reinterpret_cast<WetPix*>(srcIt.rawData()));
  121. WetPix up, down;
  122. while (!srcIt.isDone()) {
  123. up = *(reinterpret_cast<WetPix*>(upIt.rawData()));
  124. down = *(reinterpret_cast<WetPix*>(downIt.rawData()));
  125. if (current.w > 0) {
  126. my_height = current.h + current.w;
  127. ft = (up.h + up.w) - my_height;
  128. fb = (down.h + down.w) - my_height;
  129. fl = (left.h + left.w) - my_height;
  130. fr = (right.h + right.w) - my_height;
  131. fluid[ix] = 0.4 * sqrt(current.w * 1.0 / 255.0);
  132. /* smooth out the flow a bit */
  133. flow_t[ix] = CLAMP(0.1 * (10 + ft * 0.75 - fb * 0.25), 0, 1);
  134. flow_b[ix] = CLAMP(0.1 * (10 + fb * 0.75 - ft * 0.25), 0, 1);
  135. flow_l[ix] = CLAMP(0.1 * (10 + fl * 0.75 - fr * 0.25), 0, 1);
  136. flow_r[ix] = CLAMP(0.1 * (10 + fr * 0.75 - fl * 0.25), 0, 1);
  137. outflow[ix] = 0;
  138. }
  139. ++srcIt;
  140. ++upIt;
  141. ++downIt;
  142. ix++;
  143. left = current;
  144. current = right;
  145. right = *(reinterpret_cast<WetPix*>(srcIt.rawData()));
  146. }
  147. ix+=2; // one for the last pixel on the line, and one for the first of the next line
  148. }
  149. // Second iteration: Reduce flow in dry areas
  150. ix = width + 1;
  151. for (TQ_INT32 y2 = 1; y2 < height - 1; ++y2) {
  152. KisHLineIteratorPixel srcIt = src->createHLineIterator(dx + 1, dy + y2, width - 2, false);
  153. while (!srcIt.isDone()) {
  154. if ((reinterpret_cast<WetPix*>(srcIt.rawData()))->w > 0) {
  155. /* reduce flow in dry areas */
  156. flow_t[ix] *= fluid[ix] * fluid[ix - rs];
  157. outflow[ix - rs] += flow_t[ix];
  158. flow_b[ix] *= fluid[ix] * fluid[ix + rs];
  159. outflow[ix + rs] += flow_b[ix];
  160. flow_l[ix] *= fluid[ix] * fluid[ix - 1];
  161. outflow[ix - 1] += flow_l[ix];
  162. flow_r[ix] *= fluid[ix] * fluid[ix + 1];
  163. outflow[ix + 1] += flow_r[ix];
  164. }
  165. ++srcIt;
  166. ix++;
  167. }
  168. ix += 2;
  169. }
  170. // Third iteration: Combine the paint from the flow areas.
  171. ix = width + 1;
  172. for (TQ_INT32 y2 = 1; y2 < height - 1; ++y2) {
  173. KisHLineIteratorPixel srcIt = src->createHLineIterator(dx, dy + y2, width, false);
  174. KisHLineIteratorPixel upIt = src->createHLineIterator(dx + 1, dy + y2 - 1, width - 2, false);
  175. KisHLineIteratorPixel downIt = src->createHLineIterator(dx + 1, dy + y2 + 1, width - 2, false);
  176. KisHLineIteratorPixel dstIt = src->createHLineIterator(dx + 1, dy + y2, width - 2, true);
  177. WetPix left = *(reinterpret_cast<const WetPix*>(srcIt.oldRawData()));
  178. ++srcIt;
  179. WetPix current = *(reinterpret_cast<const WetPix*>(srcIt.oldRawData()));
  180. ++srcIt;
  181. WetPix right = *(reinterpret_cast<const WetPix*>(srcIt.oldRawData()));
  182. WetPix up, down;
  183. while (!srcIt.isDone()) {
  184. up = *(reinterpret_cast<const WetPix*>(upIt.oldRawData()));
  185. down = *(reinterpret_cast<const WetPix*>(downIt.oldRawData()));
  186. if ((reinterpret_cast<WetPix*>(srcIt.rawData()))->w > 0) {
  187. reducePixel(&wet_mix, &current, 1 - outflow[ix]);
  188. reducePixel(&wet_tmp, &up, flow_t[ix]);
  189. combinePixels(&wet_mix, &wet_mix, &wet_tmp);
  190. reducePixel(&wet_tmp, &down, flow_b[ix]);
  191. combinePixels(&wet_mix, &wet_mix, &wet_tmp);
  192. reducePixel(&wet_tmp, &left, flow_l[ix]);
  193. combinePixels(&wet_mix, &wet_mix, &wet_tmp);
  194. reducePixel(&wet_tmp, &right, flow_r[ix]);
  195. combinePixels(&wet_mix, &wet_mix, &wet_tmp);
  196. WetPix* target = reinterpret_cast<WetPix*>(dstIt.rawData());
  197. wetPixFromDouble(target, &wet_mix);
  198. }
  199. ++srcIt;
  200. ++dstIt;
  201. ++upIt;
  202. ++downIt;
  203. ix++;
  204. left = current;
  205. current = right;
  206. right = *(reinterpret_cast<const WetPix*>(srcIt.oldRawData()));
  207. }
  208. ix += 2;
  209. }
  210. delete[] flow_t;
  211. delete[] flow_b;
  212. delete[] flow_l;
  213. delete[] flow_r;
  214. delete[] fluid;
  215. delete[] outflow;
  216. }
  217. void WetPhysicsFilter::dry(KisPaintDeviceSP src, KisPaintDeviceSP dst, const TQRect & r)
  218. {
  219. kdDebug () << "Drying " << r << endl;
  220. for (TQ_INT32 y = 0; y < r.height(); y++) {
  221. KisHLineIteratorPixel srcIt = src->createHLineIterator(r.x(), r.y() + y, r.width(), false);
  222. KisHLineIteratorPixel dstIt = dst->createHLineIterator(r.x(), r.y() + y, r.width(), true);
  223. TQ_UINT16 w;
  224. while (!srcIt.isDone()) {
  225. // Two wet pixels in one KisWetColorSpace pixels.
  226. WetPack pack = *(reinterpret_cast<WetPack*>(srcIt.rawData()));
  227. WetPix* p = &(pack.paint);
  228. w = p->w; // no -1 here because we work on unsigned ints!
  229. if (w > 0)
  230. p->w = w - 1;
  231. else
  232. p->w = 0;
  233. *(reinterpret_cast<WetPack*>(dstIt.rawData())) = pack;
  234. ++dstIt;
  235. ++srcIt;
  236. }
  237. }
  238. }
  239. void WetPhysicsFilter::adsorb(KisPaintDeviceSP src, KisPaintDeviceSP /*dst*/, const TQRect & r)
  240. {
  241. kdDebug() << "Adsorbing " << r << endl;
  242. for (TQ_INT32 y = 0; y < r.height(); y++) {
  243. KisHLineIteratorPixel srcIt = src->createHLineIterator(r.x(), r.y() + y, r.width(), true);
  244. double ads;
  245. WetPixDbl wet_top;
  246. WetPixDbl wet_bot;
  247. WetPack * pack;
  248. TQ_UINT16 w;
  249. while (!srcIt.isDone()) {
  250. // Two wet pixels in one KisWetColorSpace pixels.
  251. pack = reinterpret_cast<WetPack*>(srcIt.rawData());
  252. WetPix* paint = &pack->paint;
  253. WetPix* adsorb = &pack->adsorb;
  254. /* do adsorption */
  255. w = paint->w;
  256. if (w == 0) {
  257. ++srcIt;
  258. }
  259. else {
  260. ads = 0.5 / TQMAX(w, 1);
  261. wetPixToDouble(&wet_top, paint);
  262. wetPixToDouble(&wet_bot, adsorb);
  263. mergePixel(&wet_bot, &wet_top, ads, &wet_bot);
  264. wetPixFromDouble(adsorb, &wet_bot);
  265. paint->rd = (TQ_UINT16) (paint->rd*(1 - ads));
  266. paint->rw = (TQ_UINT16) (paint->rw*(1 - ads));
  267. paint->gd = (TQ_UINT16) (paint->gd*(1 - ads));
  268. paint->gw = (TQ_UINT16) (paint->gw*(1 - ads));
  269. paint->bd = (TQ_UINT16) (paint->bd*(1 - ads));
  270. paint->bw = (TQ_UINT16) (paint->bw*(1 - ads));
  271. ++srcIt;
  272. }
  273. }
  274. }
  275. }
  276. void WetPhysicsFilter::combinePixels (WetPixDbl *dst, WetPixDbl *src1, WetPixDbl *src2)
  277. {
  278. dst->rd = src1->rd + src2->rd;
  279. dst->rw = src1->rw + src2->rw;
  280. dst->gd = src1->gd + src2->gd;
  281. dst->gw = src1->gw + src2->gw;
  282. dst->bd = src1->bd + src2->bd;
  283. dst->bw = src1->bw + src2->bw;
  284. dst->w = src1->w + src2->w;
  285. }
  286. void WetPhysicsFilter::dilutePixel (WetPixDbl *dst, WetPix *src, double dilution)
  287. {
  288. double scale = dilution * (1.0 / 8192.0);
  289. dst->rd = src->rd * scale;
  290. dst->rw = src->rw * scale;
  291. dst->gd = src->gd * scale;
  292. dst->gw = src->gw * scale;
  293. dst->bd = src->bd * scale;
  294. dst->bw = src->bw * scale;
  295. dst->w = src->w * (1.0 / 8192.0);
  296. dst->h = src->h * (1.0 / 8192.0);
  297. }
  298. void WetPhysicsFilter::reducePixel (WetPixDbl *dst, WetPix *src, double dilution)
  299. {
  300. dilutePixel(dst, src, dilution);
  301. dst->w *= dilution;
  302. }
  303. void WetPhysicsFilter::mergePixel (WetPixDbl *dst, WetPixDbl *src1, double dilution1,
  304. WetPixDbl *src2)
  305. {
  306. double d1, w1, d2, w2;
  307. double ed1, ed2;
  308. if (src1->rd < 1e-4) {
  309. dst->rd = src2->rd;
  310. dst->rw = src2->rw;
  311. } else if (src2->rd < 1e-4) {
  312. dst->rd = src1->rd * dilution1;
  313. dst->rw = src1->rw * dilution1;
  314. } else {
  315. d1 = src1->rd;
  316. w1 = src1->rw;
  317. d2 = src2->rd;
  318. w2 = src2->rw;
  319. dst->rd = d1 * dilution1 + d2;
  320. ed1 = exp(-d1 * dilution1);
  321. ed2 = exp(-d2);
  322. dst->rw = dst->rd * ((1 - ed1) * w1 / d1 + ed1 * (1 - ed2) * w2 / d2) / (1 - ed1 * ed2);
  323. }
  324. if (src1->gd < 1e-4) {
  325. dst->gd = src2->gd;
  326. dst->gw = src2->gw;
  327. } else if (src2->gd < 1e-4) {
  328. dst->gd = src1->gd * dilution1;
  329. dst->gw = src1->gw * dilution1;
  330. } else {
  331. d1 = src1->gd;
  332. w1 = src1->gw;
  333. d2 = src2->gd;
  334. w2 = src2->gw;
  335. dst->gd = d1 * dilution1 + d2;
  336. ed1 = exp(-d1 * dilution1);
  337. ed2 = exp(-d2);
  338. dst->gw = dst->gd * ((1 - ed1) * w1 / d1 + ed1 * (1 - ed2) * w2 / d2) / (1 - ed1 * ed2);
  339. }
  340. if (src1->bd < 1e-4) {
  341. dst->bd = src2->bd;
  342. dst->bw = src2->bw;
  343. } else if (src2->bd < 1e-4) {
  344. dst->bd = src1->bd * dilution1;
  345. dst->bw = src1->bw * dilution1;
  346. } else {
  347. d1 = src1->bd;
  348. w1 = src1->bw;
  349. d2 = src2->bd;
  350. w2 = src2->bw;
  351. dst->bd = d1 * dilution1 + d2;
  352. ed1 = exp(-d1 * dilution1);
  353. ed2 = exp(-d2);
  354. dst->bw = dst->bd * ((1 - ed1) * w1 / d1 + ed1 * (1 - ed2) * w2 / d2) / (1 - ed1 * ed2);
  355. }
  356. }