1
2
3 #include <stack>
4 #include <time.h>
5
6 #include "stdfx.h"
7 #include "tsystem.h"
8 #include "tconvert.h"
9 #include "trop.h"
10
11 /* Riferimenti:
12 [1] "Filling a region in a frame buffer", Ken Fishkin, su Graphics Gems vol.1,
13 pag 278;
14 */
15
16 // TODO
17 // v integrare in zcomp
18 // v aggiungere: selezioni multiple, antialias, feather, .
19 // v aggiungere: gestione del mouse, indicatore del colore del pixel
20 // selezionato, media sui vicini del pixel selezionato
21 // . maglass
22 // . verifiche ed ottimizzazioni
23
24 namespace {} // anonymous namespace
25
26 static int invocazioni = 0; // tmp: numero di invocazioni della MagicWand
27
28 // Shadow Segment
29 // "Ombra" proiettata da una riga di pixel idonei sulle righe adiacenti
30 // superiore ed inferiore; vedi [1].
31 class ShadowSegment {
32 public:
ShadowSegment(int Lx,int Rx,int pLx,int pRx,int y,int dir)33 ShadowSegment(int Lx, int Rx, int pLx, int pRx, int y, int dir)
34 : m_lx(Lx), m_rx(Rx), m_pLx(pLx), m_pRx(pRx), m_y(y), m_dir(dir) {}
35 int m_rx, // Right endpoint
36 m_lx, // Left endpoint
37 m_pRx, // parent Right endpoint
38 m_pLx, // parent Left endpoint
39 m_y, // this segment line
40 m_dir; // upward, downward
41 };
42
43 // Stack per la memorizzazione dei segmenti.
44 typedef std::stack<ShadowSegment> ShadowSegmentStack;
45
46 class MagicWandFx : public TStandardRasterFx {
47 FX_PLUGIN_DECLARATION(MagicWandFx)
48
49 TRasterFxPort m_input;
50 TDoubleParamP m_tolerance; // tolleranza
51 TDoubleParamP m_blurRadius; // ampiezza del campione per il SEED
52
53 TPointParamP m_point; // coordinate del SEED (passate da zviewer)
54 TBoolParamP m_contiguous; // selezione di regioni non connesse alla regione
55 // contentente il SEED
56 TBoolParamP m_antialiased; // applicazione dell'antialiasing
57 TBoolParamP m_euclideanD; // funzione alternativa per il calcolo della
58 // "similitudine" tra punti
59 TBoolParamP m_preMolt; // premoltiplicazione
60 TBoolParamP m_isShiftPressed; // per le selezioni multiple: SUB
61 TBoolParamP m_isAltPressed; // per le selezioni multiple: ADD
62
63 public:
MagicWandFx()64 MagicWandFx()
65 : m_tolerance(15.0)
66 , m_blurRadius(0.0)
67 , m_point(TPointD(0, 0))
68
69 {
70 m_contiguous = TBoolParamP(true);
71 m_antialiased = TBoolParamP(true);
72 m_euclideanD = TBoolParamP(true);
73 m_preMolt = TBoolParamP(true);
74 m_isShiftPressed = TBoolParamP(false);
75 m_isAltPressed = TBoolParamP(false);
76
77 addParam("Tolerance", m_tolerance);
78 addParam("Feather", m_blurRadius);
79 addParam("Point", m_point);
80 addParam("Contiguous", m_contiguous);
81 addParam("Antialias", m_antialiased);
82 addParam("EuclideanD", m_euclideanD);
83 addParam("PreMultiply", m_preMolt);
84 addParam("isShiftPressed", m_isShiftPressed);
85 addParam("isAltPressed", m_isAltPressed);
86 addInputPort("Source", m_input);
87
88 m_tolerance->setValueRange(0, 255);
89 m_blurRadius->setValueRange(0, 100);
90 }
91
~MagicWandFx()92 ~MagicWandFx(){};
93
94 TRect getInvalidRect(const TRect &max);
95 void doCompute(TTile &tile, double frame, const TRasterFxRenderInfo *ri);
96 void doMagicWand(TTile &tile, double frame, const TRasterFxRenderInfo *ri);
97 void EnqueueSegment(int num, int dir, int pLx, int pRx, int Lx, int Rx,
98 int y);
99 bool pixelProcessor(TPixel32 *testPix, TPixelGR8 *maskPix);
100
getBBox(double frame,TRectD & rect,TPixel32 & bgColor)101 bool getBBox(double frame, TRectD &rect, TPixel32 &bgColor) {
102 return m_input->getBBox(frame, rect, bgColor);
103 }
104
105 TRasterGR8P m_maskGR8; // maschera
106 TPixel32 *m_pickedPix; // puntatore al pixel SEED
107 TPixelGR8 *m_maskPickedPix;
108
109 int m_imageHeigth; // altezza del raster
110 int m_imageWidth; // larghezza del raster
111 double m_tol; // le uso per evitare di dover richiamare la funzione getValue
112 // per ogni punto: sistemare?
113 int m_cont; // le uso per evitare di dover richiamare la funzione getValue
114 // per ogni punto: sistemare?
115 bool m_antial; // le uso per evitare di dover richiamare la funzione getValue
116 // per ogni punto: sistemare?
117 bool m_euclid; // le uso per evitare di dover richiamare la funzione getValue
118 // per ogni punto: sistemare?
119 bool m_add;
120 bool m_sub;
121 int m_id_invocazione; // contatore delle invocazioni
122 ShadowSegmentStack m_sSStack; // stack dei segmenti shadow
123 };
124
125 const int EmptyPixel = 0;
126 const int FullPixel = 255;
127
128 //--------------------------------------
129
130 // tmp: per l'analisi delle prestazioni
131 int pixelProcessed;
132 int pixelMasked;
133 int shadowEnqueued;
134 int pixelReprocessed;
135 int shadowOutOfBorder;
136 bool maskValue;
137
pixelProcessor(TPixel32 * testPix,TPixelGR8 * maskPix)138 bool MagicWandFx::pixelProcessor(TPixel32 *testPix, TPixelGR8 *maskPix) {
139 pixelProcessed++;
140 unsigned int maskValue = 0;
141 double diff = 0;
142
143 // valuto la distanza tra il testPix ed il SEED e la metto in diff
144 if (m_euclid) {
145 // calcolo la Distanza Euclidea tra i punti nello spazio RGB
146 diff = sqrt((m_pickedPix->r - testPix->r) * (m_pickedPix->r - testPix->r) +
147 (m_pickedPix->g - testPix->g) * (m_pickedPix->g - testPix->g) +
148 (m_pickedPix->b - testPix->b) * (m_pickedPix->b - testPix->b));
149 } else {
150 // GIMP-like: confronto la tolleranza con il massimo tra gli scarti delle
151 // componenti
152 diff = abs(m_pickedPix->r - testPix->r);
153 double diffNext = abs(m_pickedPix->g - testPix->g);
154 if (diffNext >= diff) diff = diffNext;
155 diffNext = abs(m_pickedPix->b - testPix->b);
156 if (diffNext >= diff) diff = diffNext;
157 }
158
159 if (diff <= m_tol)
160 maskValue = FullPixel;
161 else
162 maskValue = EmptyPixel;
163
164 if (maskValue) {
165 // il pixel soddisfa il criterio di compatibilita
166
167 if (m_add) {
168 // sto aggiungendo selezioni
169 if (testPix->m != EmptyPixel) {
170 pixelReprocessed++;
171 return false;
172 } // gia' trattato
173
174 //* DECIDERE SE VOGLIO CHE LA SELEZIONE INTERESSI AREE GIA' SELEZIONATE IN
175 // PRECEDENZA
176 // if (maskPix->value == EmptyPixel) { //pixel c-compatibile, non
177 // gia' mascherato
178 testPix->m = maskValue; // set(mV)m
179 maskPix->value = maskValue; // set(mV)a
180 pixelMasked++;
181 // } else { // pixel c-compatibile gia' mascherato precedentemente
182 // testPix->m = maskValue; // set(mV)m
183 // }
184 } else if (m_sub) {
185 // sto togliendo selezioni
186 if (testPix->m != EmptyPixel) return false; // gia' trattato
187 testPix->m = maskValue; // set(mV)m
188 maskPix->value = EmptyPixel; // set(0)a
189 } else { // prima selezione
190 if (testPix->m != EmptyPixel) return false; // gia' trattato
191 testPix->m = maskValue; // set(mV)m
192 maskPix->value = maskValue; // set(mV)a
193 pixelMasked++;
194 }
195 return true;
196 } else
197 return false;
198 } // pixelProcessor
199
200 // [1]: aggiunge le ombre necessarie alla pila.
EnqueueSegment(int num,int dir,int pLx,int pRx,int Lx,int Rx,int y)201 void MagicWandFx::EnqueueSegment(int num, int dir, int pLx, int pRx, int Lx,
202 int Rx, int y) {
203 int pushRx = Rx + 1;
204 int pushLx = Lx + 1;
205
206 // TSystem::outputDebug("[MWfx("+toString(m_id_invocazione)+":"+toString(num)+")<PUSH
207 // 1>]\tStack Size:"+toString((int)
208 // m_sSStack.size())+"\tLx:"+toString(Lx)+"\tRx:"+toString(Rx)+"\tpLx:"+toString(pushLx)+"\tpRx:"+toString(pushRx)+"\ty:"+toString(y)+"\tdir:"+toString(dir)+"\n");
209 assert((Lx <= Rx) && (pushLx <= pushRx) && (Lx >= 0));
210 m_sSStack.push(ShadowSegment(Lx, Rx, pushLx, pushRx, (y + dir), dir));
211 shadowEnqueued++;
212
213 if (Rx > pRx) { // U-turn a destra
214 // TSystem::outputDebug("[MWfx("+toString(m_id_invocazione)+":"+toString(num)+")<PUSH
215 // 2>]\tStack Size:"+toString((int)
216 // m_sSStack.size())+"\tLx:"+toString(pRx+1)+"\tRx:"+toString(Rx)+"\tpLx:"+toString(pushLx)+"\tpRx:"+toString(pushRx)+"\ty:"+toString(y-dir)+"\tdir:"+toString(dir)+"\n");
217 assert(((pRx + 1) <= (Rx)) && (pushLx <= pushRx) && ((pRx + 1) >= 0));
218 m_sSStack.push(
219 ShadowSegment((pRx + 1), Rx, pushLx, pushRx, (y - dir), (-dir)));
220 shadowEnqueued++;
221 }
222 if (Lx < pLx) { // U-turn a sinistra
223 // TSystem::outputDebug("[MWfx("+toString(m_id_invocazione)+":"+toString(num)+")<PUSH
224 // 3>]\tStack Size:"+toString((int)
225 // m_sSStack.size())+"\tLx:"+toString(Lx)+"\tRx:"+toString(pLx-1)+"\tpLx:"+toString(pushLx)+"\tpRx:"+toString(pushRx)+"\ty:"+toString(y-dir)+"\tdir:"+toString(dir)+"\n");
226 assert(((Lx) <= (pLx - 1)) && (pushLx <= pushRx) && (Lx >= 0));
227 m_sSStack.push(
228 ShadowSegment(Lx, (pLx - 1), pushLx, pushRx, (y - dir), (-dir)));
229 shadowEnqueued++;
230 }
231 // W-Turn = 2 U-Turn
232 } // EnqueueSegment
233
doMagicWand(TTile & tile,double frame,const TRasterFxRenderInfo * ri)234 void MagicWandFx::doMagicWand(TTile &tile, double frame,
235 const TRasterFxRenderInfo *ri) {
236 clock_t start_time = clock(); // debug
237 clock_t stop_time;
238
239 invocazioni++;
240 m_id_invocazione = invocazioni;
241 m_tol = m_tolerance->getValue(frame);
242 m_antial = m_antialiased->getValue();
243 m_euclid = m_euclideanD->getValue(); // temporaneo?
244 m_cont = m_contiguous->getValue(); // selezione di aree cromaticamente
245 // compatibili ma non contigue: Selezione
246 // ByColor
247 m_add = m_isShiftPressed->getValue();
248 m_sub = m_isAltPressed->getValue();
249
250 tile.getRaster()->lock();
251 TRaster32P ras32 = tile.getRaster();
252
253 TPixel32 vPixel;
254 TPixel32 *tmpPix;
255 TPixel32 *rowStart;
256 TPixelGR8 *maskRowStart;
257 TPixelGR8 *maskPix;
258
259 if (ras32) {
260 pixelProcessed = 0;
261 pixelMasked = 1;
262 shadowEnqueued = 2;
263 pixelReprocessed = 0;
264 shadowOutOfBorder = 0;
265
266 m_imageWidth = ras32->getLx();
267 m_imageHeigth = ras32->getLy();
268 // assert(m_imageWidth == 800);
269 assert(m_imageHeigth <= 600);
270 int lx = m_imageWidth;
271 int ly = m_imageHeigth;
272
273 if (!m_maskGR8) {
274 // prima esecuzione creo il raster gr8 x la maschera e azzero gli alpha
275 TRectD bBoxD;
276 TPixel32 bgColor;
277 bool getBBoxOk = getBBox(frame, bBoxD, bgColor);
278 assert(getBBoxOk);
279 TRect bBoxI = convert(bBoxD);
280 m_maskGR8 = TRasterGR8P(bBoxI.getLx(), bBoxI.getLy());
281 m_maskGR8->clear();
282 }
283 m_maskGR8->lock();
284 // sono arrivato qui: sto verificando se serve davvero il gr8.
285
286 for (int iy = 0; iy < m_imageHeigth; iy++) { // y
287 tmpPix = ras32->pixels(iy);
288 for (int ix = 0; ix < m_imageWidth; ix++) { // x
289 tmpPix->m = EmptyPixel;
290 tmpPix++;
291 } // x
292 } // y
293
294 if (m_add) { // ho premuto Shift sto aggiungendo alla selezione
295
296 } else if (m_sub) {
297 // ho premuto Alt sto sottraendo dalla selezione
298
299 } else {
300 // non ho premuto niente nuova selezione
301
302 // ripulisco il canale alpha dell'immagine e la maschera
303 for (int iy = 0; iy < m_imageHeigth; iy++) {
304 tmpPix = ras32->pixels(iy);
305 maskPix = m_maskGR8->pixels(iy);
306 for (int ix = 0; ix < m_imageWidth; ix++) {
307 tmpPix->m = 0;
308
309 maskPix->value = EmptyPixel;
310 tmpPix++;
311 maskPix++;
312 } // x
313 } // y
314 }
315
316 // trovo il pixel in X,Y soluzione temporanea in attesa della gestione del
317 // mouse.
318 // converto le coordinate mondo (-500;+500) in coordinate raster
319 // (0;m_imageWidth);
320 TPointD point = m_point->getValue(frame);
321
322 // coordinate dagli sliders
323 // int x = (int) (500+point.x)*m_imageWidth/1000; if (x>0) x--;
324 // int y = (int) (500+point.y)*m_imageHeigth/1000; if (y>0) y--;
325
326 // coordinate dalla ZViewer:leftButtonClick
327
328 int x = tcrop((int)(point.x + m_imageWidth / 2), 0, (m_imageWidth - 1));
329 int y = tcrop((int)(point.y + m_imageHeigth / 2), 0, (m_imageHeigth - 1));
330
331 TSystem::outputDebug("\n[MWfx(" + toString(m_id_invocazione) +
332 ")<begin>]\nSize:" + toString(m_imageWidth) + "x" +
333 toString(m_imageHeigth) + "\tx:" + toString(x) +
334 "\ty:" + toString(y) + "\tToll:" + toString(m_tol) +
335 /* "\tRadius:" + toString(radius) +*/ (
336 (m_cont) ? "\tContiguous" : "\tNon Contiguous") +
337 ((m_antial) ? "\tAnti Aliased" : "\tAliased") +
338 ((m_euclid) ? "\tEuclidean\n" : "\tNon Euclidean\n"));
339
340 lx = m_imageWidth;
341 ly = m_imageHeigth;
342
343 m_pickedPix = ras32->pixels(y) + x;
344 m_maskPickedPix = m_maskGR8->pixels(y) + x;
345
346 pixelProcessed = 1;
347
348 if (m_cont) { // seleziono esclusivamente i pixel connessi al pixel SEED
349
350 //- ALGORITMO FLOOD FILL: GRAPHICS GEM 1 p.280
351 //----------------------------------------
352 int xAux, yAux, lxAux, rxAux, dirAux, pRxAux, pLxAux;
353 bool inSpan = true;
354
355 // trova Rx e Lx dello span contentente il SEED point
356 int xCont = x;
357 tmpPix = m_pickedPix; // puntatore al SEED
358 maskPix = m_maskPickedPix; //******
359
360 // cerco Lx
361 maskValue = pixelProcessor(tmpPix, maskPix);
362 bool tmpMv = maskValue;
363 while ((xCont >= 0) && (maskValue)) {
364 tmpPix--;
365 maskPix--;
366 xCont--;
367 if (xCont >= 0) maskValue = pixelProcessor(tmpPix, maskPix);
368 }
369 if (tmpMv)
370 lxAux = xCont + 1;
371 else
372 lxAux = xCont;
373
374 // cerco Rx
375 tmpPix = m_pickedPix;
376 maskPix = m_maskPickedPix; //******
377
378 xCont = x;
379 maskValue = tmpMv;
380 while ((xCont < m_imageWidth) && (maskValue)) {
381 tmpPix++;
382 maskPix++;
383 xCont++;
384 if (xCont < m_imageWidth) maskValue = pixelProcessor(tmpPix, maskPix);
385 }
386 if (tmpMv)
387 rxAux = xCont - 1;
388 else
389 rxAux = xCont;
390
391 assert((lxAux <= rxAux) && (lxAux >= 0));
392
393 // metto nella pila delle ombre la riga sopra e sotto quella contentente
394 // il seed.
395 // TSystem::outputDebug("[MWfx("+toString(m_id_invocazione)+")]\tStack
396 // Size:"+toString((int)
397 // m_sSStack.size())+"\tLx:"+toString(lxAux)+"\tRx:"+toString(rxAux)+"\tpLx:"+toString(lxAux)+"\tpRx:"+toString(rxAux)+"\ty:"+toString(y+1)+"\tdir:"+toString(1)+"\n");
398 m_sSStack.push(ShadowSegment(lxAux, rxAux, lxAux, rxAux, y + 1,
399 +1)); // cerca in alto
400 // TSystem::outputDebug("[MWfx("+toString(m_id_invocazione)+")]\tStack
401 // Size:"+toString((int)
402 // m_sSStack.size())+"\tLx:"+toString(lxAux)+"\tRx:"+toString(rxAux)+"\tpLx:"+toString(lxAux)+"\tpRx:"+toString(rxAux)+"\ty:"+toString(y-1)+"\tdir:"+toString(-1)+"\n");
403 m_sSStack.push(ShadowSegment(lxAux, rxAux, lxAux, rxAux, y - 1,
404 -1)); // cerca in basso
405
406 while (!m_sSStack.empty()) {
407 ShadowSegment sSegment = m_sSStack.top();
408 m_sSStack.pop();
409 // TSystem::outputDebug("[MWfx("+toString(m_id_invocazione)+":0)<POP
410 // >]\tStack Size:"+toString((int)
411 // m_sSStack.size())+"\tLx:"+toString(sSegment.m_lx)+"\tRx:"+toString(sSegment.m_rx)+"\tpLx:"+toString(sSegment.m_pLx)+"\tpRx:"+toString(sSegment.m_pRx)+"\ty:"+toString(sSegment.m_y)+"\tdir:"+toString(sSegment.m_dir)+"\n");
412
413 dirAux = sSegment.m_dir;
414 pRxAux = sSegment.m_pRx;
415 pLxAux = sSegment.m_pLx;
416 lxAux = sSegment.m_lx;
417 rxAux = sSegment.m_rx;
418 yAux = sSegment.m_y;
419
420 if ((yAux < 0) || (yAux >= m_imageHeigth)) {
421 shadowOutOfBorder++;
422 continue; // questo segmento sta fuori dal raster oppure l'ho gia'
423 // colorato: lo salto
424 }
425 assert((lxAux <= rxAux) && (pLxAux <= pRxAux));
426 assert((m_sSStack.size() <= 1000));
427
428 xAux = lxAux + 1;
429
430 rowStart = ras32->pixels(yAux);
431
432 maskRowStart = m_maskGR8->pixels(yAux); //**
433
434 tmpPix = rowStart + lxAux;
435 maskPix = maskRowStart + lxAux;
436
437 maskValue = pixelProcessor(tmpPix, maskPix);
438
439 inSpan = (maskValue);
440
441 if (maskValue) { // il punto e' cromaticompatibile
442 lxAux--;
443 if (lxAux >= 0) {
444 tmpPix--;
445 maskPix--;
446 maskValue = pixelProcessor(tmpPix, maskPix);
447 }
448 while ((maskValue) &&
449 (lxAux >= 0)) { // sto nello span E nell'immagine
450 lxAux--;
451 if (lxAux >= 0) {
452 tmpPix--;
453 maskPix--;
454 maskValue = pixelProcessor(tmpPix, maskPix);
455 }
456 } // sto nello span E nell'immagine
457 } // il punto e' cromaticompatibile
458 lxAux++;
459 // rowStart = ras32->pixels(yAux);
460 while (xAux < m_imageWidth) { // mi sposto a destra lungo la X
461 if (inSpan) {
462 tmpPix = rowStart + xAux;
463 maskPix = maskRowStart + xAux; //***
464 maskValue = pixelProcessor(tmpPix, maskPix);
465 if (maskValue) { // case 1
466 // fa tutto nella pixel processor
467 } // case 1
468 else { // case 2
469 EnqueueSegment(1, dirAux, pLxAux, pRxAux, lxAux, (xAux - 1),
470 yAux);
471 inSpan = false;
472 } // case 2
473 } // inSpan
474 else { // non ero nello span
475 if (xAux > rxAux) break;
476 tmpPix = rowStart + xAux;
477 maskPix = maskRowStart + xAux;
478 maskValue = pixelProcessor(tmpPix, maskPix);
479 if (maskValue) { // case 3
480 inSpan = true;
481 lxAux = xAux;
482 } // case 3
483 else { // case 4
484 }
485 } // non ero nello span
486 xAux++;
487 // TSystem::outputDebug("[MWfx("+toString(m_id_invocazione)+")]\tStack
488 // Size:"+toString((int)
489 // m_sSStack.size())+"\txAux:"+toString(xAux)+"\ty:"+toString(yAux)+"\n");
490 } // mi sposto a destra lungo la X: endloop 1
491 if (inSpan) {
492 EnqueueSegment(2, dirAux, pLxAux, pRxAux, lxAux, (xAux - 1), yAux);
493 }
494 } // finche' la pila non e' vuota: endloop 2
495 } // if m_cont
496 else { // anche le regioni simili NON contigue: questo rimane anche in caso
497 // di modifica della parte m_cont
498 for (int iy = 0; iy < m_imageHeigth; iy++) {
499 tmpPix = ras32->pixels(iy);
500 maskPix = m_maskGR8->pixels(iy);
501 for (int ix = 0; ix < m_imageWidth; ix++) {
502 maskValue = pixelProcessor(tmpPix, maskPix);
503 // if (maskValue) { } // if// il colore e' simile =>
504 // va incluso nella selezione // fa tutto nella pixel
505 // processor
506 tmpPix++;
507 maskPix++;
508 } // ix
509 } // iy
510 } // else m_cont
511
512 int blurRadius = (int)(m_blurRadius->getValue(frame));
513 if ((m_antial) && (blurRadius < 2)) blurRadius = 1;
514
515 if (blurRadius > 0)
516 TRop::blur(m_maskGR8, m_maskGR8, (blurRadius + 1), 0, 0);
517
518 // copio la maschera sull'alpha channel dell'immagine
519 // lo faccio a mano chiedere se esiste una funziona apposita
520 for (iy = 0; iy < m_imageHeigth; iy++) {
521 tmpPix = ras32->pixels(iy);
522 maskPix = m_maskGR8->pixels(iy);
523 for (int ix = 0; ix < m_imageWidth; ix++) {
524 tmpPix->m = maskPix->value;
525 tmpPix++;
526 maskPix++;
527 } // ix
528 } // iy
529
530 if (m_preMolt->getValue()) TRop::premultiply(ras32);
531 stop_time = clock();
532 double durata = (double)(stop_time - start_time) / CLOCKS_PER_SEC;
533
534 TSystem::outputDebug(
535 "\n#Pixel:\t" + toString(m_imageWidth * m_imageHeigth) + "\nProc:\t" +
536 toString(pixelProcessed) + "\t[" +
537 toString((pixelProcessed * 100 / (m_imageWidth * m_imageHeigth))) +
538 "%t]" + "\nMask:\t" + toString(pixelMasked) + "\t[" +
539 toString((pixelMasked * 100 / (m_imageWidth * m_imageHeigth))) + "%t]" +
540 "\t[" + toString((pixelMasked * 100 / (pixelProcessed))) + "%p]" +
541 "\nEnqu:\t" + toString(shadowEnqueued) + "\nRepr:\t" +
542 toString(pixelReprocessed) + "\t[" +
543 toString((pixelReprocessed * 100 / (m_imageWidth * m_imageHeigth))) +
544 "%t]" + "\t[" + toString((pixelReprocessed * 100 / (pixelProcessed))) +
545 "%p]" + "\nOutB:\t" + toString(shadowOutOfBorder) + "\t[" +
546 toString((shadowOutOfBorder * 100 / (shadowEnqueued))) + "%t]" +
547 "\nTime:\t" + toString(durata, 3) + " sec\n[MagicWandFX <end>]\n");
548
549 } // if (ras32)
550 else {
551 TRasterGR8P rasGR8 = tile.getRaster();
552 if (rasGR8) {
553 }
554 }
555 tile.getRaster()->unlock();
556 m_maskGR8->unlock();
557 } // doMagicWand
558
doCompute(TTile & tile,double frame,const TRasterFxRenderInfo * ri)559 void MagicWandFx::doCompute(TTile &tile, double frame,
560 const TRasterFxRenderInfo *ri) {
561 if (!m_input.isConnected()) return;
562
563 m_input->compute(tile, frame, ri);
564
565 doMagicWand(tile, frame, ri);
566 }
567
568 //------------------------------------------------------------------
569
getInvalidRect(const TRect & max)570 TRect MagicWandFx::getInvalidRect(const TRect &max) { return max; }
571
572 //------------------------------------------------------------------
573
574 FX_PLUGIN_IDENTIFIER(MagicWandFx, magicWandFx);
575