| |
| |
| #include <stack> |
| #include <time.h> |
| |
| #include "stdfx.h" |
| #include "tsystem.h" |
| #include "tconvert.h" |
| #include "trop.h" |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| namespace {} |
| |
| static int invocazioni = 0; |
| |
| |
| |
| |
| class ShadowSegment { |
| public: |
| ShadowSegment(int Lx, int Rx, int pLx, int pRx, int y, int dir) |
| : m_lx(Lx), m_rx(Rx), m_pLx(pLx), m_pRx(pRx), m_y(y), m_dir(dir) {} |
| int m_rx, |
| m_lx, |
| m_pRx, |
| m_pLx, |
| m_y, |
| m_dir; |
| }; |
| |
| |
| typedef std::stack<ShadowSegment> ShadowSegmentStack; |
| |
| class MagicWandFx : public TStandardRasterFx { |
| FX_PLUGIN_DECLARATION(MagicWandFx) |
| |
| TRasterFxPort m_input; |
| TDoubleParamP m_tolerance; |
| TDoubleParamP m_blurRadius; |
| |
| TPointParamP m_point; |
| TBoolParamP m_contiguous; |
| |
| TBoolParamP m_antialiased; |
| TBoolParamP m_euclideanD; |
| |
| TBoolParamP m_preMolt; |
| TBoolParamP m_isShiftPressed; |
| TBoolParamP m_isAltPressed; |
| |
| public: |
| MagicWandFx() |
| : m_tolerance(15.0) |
| , m_blurRadius(0.0) |
| , m_point(TPointD(0, 0)) |
| |
| { |
| m_contiguous = TBoolParamP(true); |
| m_antialiased = TBoolParamP(true); |
| m_euclideanD = TBoolParamP(true); |
| m_preMolt = TBoolParamP(true); |
| m_isShiftPressed = TBoolParamP(false); |
| m_isAltPressed = TBoolParamP(false); |
| |
| addParam("Tolerance", m_tolerance); |
| addParam("Feather", m_blurRadius); |
| addParam("Point", m_point); |
| addParam("Contiguous", m_contiguous); |
| addParam("Antialias", m_antialiased); |
| addParam("EuclideanD", m_euclideanD); |
| addParam("PreMultiply", m_preMolt); |
| addParam("isShiftPressed", m_isShiftPressed); |
| addParam("isAltPressed", m_isAltPressed); |
| addInputPort("Source", m_input); |
| |
| m_tolerance->setValueRange(0, 255); |
| m_blurRadius->setValueRange(0, 100); |
| } |
| |
| ~MagicWandFx(){}; |
| |
| TRect getInvalidRect(const TRect &max); |
| void doCompute(TTile &tile, double frame, const TRasterFxRenderInfo *ri); |
| void doMagicWand(TTile &tile, double frame, const TRasterFxRenderInfo *ri); |
| void EnqueueSegment(int num, int dir, int pLx, int pRx, int Lx, int Rx, |
| int y); |
| bool pixelProcessor(TPixel32 *testPix, TPixelGR8 *maskPix); |
| |
| bool getBBox(double frame, TRectD &rect, TPixel32 &bgColor) { |
| return m_input->getBBox(frame, rect, bgColor); |
| } |
| |
| TRasterGR8P m_maskGR8; |
| TPixel32 *m_pickedPix; |
| TPixelGR8 *m_maskPickedPix; |
| |
| int m_imageHeigth; |
| int m_imageWidth; |
| double m_tol; |
| |
| int m_cont; |
| |
| bool m_antial; |
| |
| bool m_euclid; |
| |
| bool m_add; |
| bool m_sub; |
| int m_id_invocazione; |
| ShadowSegmentStack m_sSStack; |
| }; |
| |
| const int EmptyPixel = 0; |
| const int FullPixel = 255; |
| |
| |
| |
| |
| int pixelProcessed; |
| int pixelMasked; |
| int shadowEnqueued; |
| int pixelReprocessed; |
| int shadowOutOfBorder; |
| bool maskValue; |
| |
| bool MagicWandFx::pixelProcessor(TPixel32 *testPix, TPixelGR8 *maskPix) { |
| pixelProcessed++; |
| unsigned int maskValue = 0; |
| double diff = 0; |
| |
| |
| if (m_euclid) { |
| |
| diff = sqrt((m_pickedPix->r - testPix->r) * (m_pickedPix->r - testPix->r) + |
| (m_pickedPix->g - testPix->g) * (m_pickedPix->g - testPix->g) + |
| (m_pickedPix->b - testPix->b) * (m_pickedPix->b - testPix->b)); |
| } else { |
| |
| |
| diff = abs(m_pickedPix->r - testPix->r); |
| double diffNext = abs(m_pickedPix->g - testPix->g); |
| if (diffNext >= diff) diff = diffNext; |
| diffNext = abs(m_pickedPix->b - testPix->b); |
| if (diffNext >= diff) diff = diffNext; |
| } |
| |
| if (diff <= m_tol) |
| maskValue = FullPixel; |
| else |
| maskValue = EmptyPixel; |
| |
| if (maskValue) { |
| |
| |
| if (m_add) { |
| |
| if (testPix->m != EmptyPixel) { |
| pixelReprocessed++; |
| return false; |
| } |
| |
| |
| |
| |
| |
| testPix->m = maskValue; |
| maskPix->value = maskValue; |
| pixelMasked++; |
| |
| |
| |
| } else if (m_sub) { |
| |
| if (testPix->m != EmptyPixel) return false; |
| testPix->m = maskValue; |
| maskPix->value = EmptyPixel; |
| } else { |
| if (testPix->m != EmptyPixel) return false; |
| testPix->m = maskValue; |
| maskPix->value = maskValue; |
| pixelMasked++; |
| } |
| return true; |
| } else |
| return false; |
| } |
| |
| |
| void MagicWandFx::EnqueueSegment(int num, int dir, int pLx, int pRx, int Lx, |
| int Rx, int y) { |
| int pushRx = Rx + 1; |
| int pushLx = Lx + 1; |
| |
| |
| |
| |
| assert((Lx <= Rx) && (pushLx <= pushRx) && (Lx >= 0)); |
| m_sSStack.push(ShadowSegment(Lx, Rx, pushLx, pushRx, (y + dir), dir)); |
| shadowEnqueued++; |
| |
| if (Rx > pRx) { |
| |
| |
| |
| assert(((pRx + 1) <= (Rx)) && (pushLx <= pushRx) && ((pRx + 1) >= 0)); |
| m_sSStack.push( |
| ShadowSegment((pRx + 1), Rx, pushLx, pushRx, (y - dir), (-dir))); |
| shadowEnqueued++; |
| } |
| if (Lx < pLx) { |
| |
| |
| |
| assert(((Lx) <= (pLx - 1)) && (pushLx <= pushRx) && (Lx >= 0)); |
| m_sSStack.push( |
| ShadowSegment(Lx, (pLx - 1), pushLx, pushRx, (y - dir), (-dir))); |
| shadowEnqueued++; |
| } |
| |
| } |
| |
| void MagicWandFx::doMagicWand(TTile &tile, double frame, |
| const TRasterFxRenderInfo *ri) { |
| clock_t start_time = clock(); |
| clock_t stop_time; |
| |
| invocazioni++; |
| m_id_invocazione = invocazioni; |
| m_tol = m_tolerance->getValue(frame); |
| m_antial = m_antialiased->getValue(); |
| m_euclid = m_euclideanD->getValue(); |
| m_cont = m_contiguous->getValue(); |
| |
| |
| m_add = m_isShiftPressed->getValue(); |
| m_sub = m_isAltPressed->getValue(); |
| |
| tile.getRaster()->lock(); |
| TRaster32P ras32 = tile.getRaster(); |
| |
| TPixel32 vPixel; |
| TPixel32 *tmpPix; |
| TPixel32 *rowStart; |
| TPixelGR8 *maskRowStart; |
| TPixelGR8 *maskPix; |
| |
| if (ras32) { |
| pixelProcessed = 0; |
| pixelMasked = 1; |
| shadowEnqueued = 2; |
| pixelReprocessed = 0; |
| shadowOutOfBorder = 0; |
| |
| m_imageWidth = ras32->getLx(); |
| m_imageHeigth = ras32->getLy(); |
| |
| assert(m_imageHeigth <= 600); |
| int lx = m_imageWidth; |
| int ly = m_imageHeigth; |
| |
| if (!m_maskGR8) { |
| |
| TRectD bBoxD; |
| TPixel32 bgColor; |
| bool getBBoxOk = getBBox(frame, bBoxD, bgColor); |
| assert(getBBoxOk); |
| TRect bBoxI = convert(bBoxD); |
| m_maskGR8 = TRasterGR8P(bBoxI.getLx(), bBoxI.getLy()); |
| m_maskGR8->clear(); |
| } |
| m_maskGR8->lock(); |
| |
| |
| for (int iy = 0; iy < m_imageHeigth; iy++) { |
| tmpPix = ras32->pixels(iy); |
| for (int ix = 0; ix < m_imageWidth; ix++) { |
| tmpPix->m = EmptyPixel; |
| tmpPix++; |
| } |
| } |
| |
| if (m_add) { |
| |
| } else if (m_sub) { |
| |
| |
| } else { |
| |
| |
| |
| for (int iy = 0; iy < m_imageHeigth; iy++) { |
| tmpPix = ras32->pixels(iy); |
| maskPix = m_maskGR8->pixels(iy); |
| for (int ix = 0; ix < m_imageWidth; ix++) { |
| tmpPix->m = 0; |
| |
| maskPix->value = EmptyPixel; |
| tmpPix++; |
| maskPix++; |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| TPointD point = m_point->getValue(frame); |
| |
| |
| |
| |
| |
| |
| |
| int x = tcrop((int)(point.x + m_imageWidth / 2), 0, (m_imageWidth - 1)); |
| int y = tcrop((int)(point.y + m_imageHeigth / 2), 0, (m_imageHeigth - 1)); |
| |
| TSystem::outputDebug("\n[MWfx(" + toString(m_id_invocazione) + |
| ")<begin>]\nSize:" + toString(m_imageWidth) + "x" + |
| toString(m_imageHeigth) + "\tx:" + toString(x) + |
| "\ty:" + toString(y) + "\tToll:" + toString(m_tol) + |
| ( |
| (m_cont) ? "\tContiguous" : "\tNon Contiguous") + |
| ((m_antial) ? "\tAnti Aliased" : "\tAliased") + |
| ((m_euclid) ? "\tEuclidean\n" : "\tNon Euclidean\n")); |
| |
| lx = m_imageWidth; |
| ly = m_imageHeigth; |
| |
| m_pickedPix = ras32->pixels(y) + x; |
| m_maskPickedPix = m_maskGR8->pixels(y) + x; |
| |
| pixelProcessed = 1; |
| |
| if (m_cont) { |
| |
| |
| |
| int xAux, yAux, lxAux, rxAux, dirAux, pRxAux, pLxAux; |
| bool inSpan = true; |
| |
| |
| int xCont = x; |
| tmpPix = m_pickedPix; |
| maskPix = m_maskPickedPix; |
| |
| |
| maskValue = pixelProcessor(tmpPix, maskPix); |
| bool tmpMv = maskValue; |
| while ((xCont >= 0) && (maskValue)) { |
| tmpPix--; |
| maskPix--; |
| xCont--; |
| if (xCont >= 0) maskValue = pixelProcessor(tmpPix, maskPix); |
| } |
| if (tmpMv) |
| lxAux = xCont + 1; |
| else |
| lxAux = xCont; |
| |
| |
| tmpPix = m_pickedPix; |
| maskPix = m_maskPickedPix; |
| |
| xCont = x; |
| maskValue = tmpMv; |
| while ((xCont < m_imageWidth) && (maskValue)) { |
| tmpPix++; |
| maskPix++; |
| xCont++; |
| if (xCont < m_imageWidth) maskValue = pixelProcessor(tmpPix, maskPix); |
| } |
| if (tmpMv) |
| rxAux = xCont - 1; |
| else |
| rxAux = xCont; |
| |
| assert((lxAux <= rxAux) && (lxAux >= 0)); |
| |
| |
| |
| |
| |
| |
| m_sSStack.push(ShadowSegment(lxAux, rxAux, lxAux, rxAux, y + 1, |
| +1)); |
| |
| |
| |
| m_sSStack.push(ShadowSegment(lxAux, rxAux, lxAux, rxAux, y - 1, |
| -1)); |
| |
| while (!m_sSStack.empty()) { |
| ShadowSegment sSegment = m_sSStack.top(); |
| m_sSStack.pop(); |
| |
| |
| |
| |
| dirAux = sSegment.m_dir; |
| pRxAux = sSegment.m_pRx; |
| pLxAux = sSegment.m_pLx; |
| lxAux = sSegment.m_lx; |
| rxAux = sSegment.m_rx; |
| yAux = sSegment.m_y; |
| |
| if ((yAux < 0) || (yAux >= m_imageHeigth)) { |
| shadowOutOfBorder++; |
| continue; |
| |
| } |
| assert((lxAux <= rxAux) && (pLxAux <= pRxAux)); |
| assert((m_sSStack.size() <= 1000)); |
| |
| xAux = lxAux + 1; |
| |
| rowStart = ras32->pixels(yAux); |
| |
| maskRowStart = m_maskGR8->pixels(yAux); |
| |
| tmpPix = rowStart + lxAux; |
| maskPix = maskRowStart + lxAux; |
| |
| maskValue = pixelProcessor(tmpPix, maskPix); |
| |
| inSpan = (maskValue); |
| |
| if (maskValue) { |
| lxAux--; |
| if (lxAux >= 0) { |
| tmpPix--; |
| maskPix--; |
| maskValue = pixelProcessor(tmpPix, maskPix); |
| } |
| while ((maskValue) && |
| (lxAux >= 0)) { |
| lxAux--; |
| if (lxAux >= 0) { |
| tmpPix--; |
| maskPix--; |
| maskValue = pixelProcessor(tmpPix, maskPix); |
| } |
| } |
| } |
| lxAux++; |
| |
| while (xAux < m_imageWidth) { |
| if (inSpan) { |
| tmpPix = rowStart + xAux; |
| maskPix = maskRowStart + xAux; |
| maskValue = pixelProcessor(tmpPix, maskPix); |
| if (maskValue) { |
| |
| } |
| else { |
| EnqueueSegment(1, dirAux, pLxAux, pRxAux, lxAux, (xAux - 1), |
| yAux); |
| inSpan = false; |
| } |
| } |
| else { |
| if (xAux > rxAux) break; |
| tmpPix = rowStart + xAux; |
| maskPix = maskRowStart + xAux; |
| maskValue = pixelProcessor(tmpPix, maskPix); |
| if (maskValue) { |
| inSpan = true; |
| lxAux = xAux; |
| } |
| else { |
| } |
| } |
| xAux++; |
| |
| |
| |
| } |
| if (inSpan) { |
| EnqueueSegment(2, dirAux, pLxAux, pRxAux, lxAux, (xAux - 1), yAux); |
| } |
| } |
| } |
| else { |
| |
| for (int iy = 0; iy < m_imageHeigth; iy++) { |
| tmpPix = ras32->pixels(iy); |
| maskPix = m_maskGR8->pixels(iy); |
| for (int ix = 0; ix < m_imageWidth; ix++) { |
| maskValue = pixelProcessor(tmpPix, maskPix); |
| |
| |
| |
| tmpPix++; |
| maskPix++; |
| } |
| } |
| } |
| |
| int blurRadius = (int)(m_blurRadius->getValue(frame)); |
| if ((m_antial) && (blurRadius < 2)) blurRadius = 1; |
| |
| if (blurRadius > 0) |
| TRop::blur(m_maskGR8, m_maskGR8, (blurRadius + 1), 0, 0); |
| |
| |
| |
| for (iy = 0; iy < m_imageHeigth; iy++) { |
| tmpPix = ras32->pixels(iy); |
| maskPix = m_maskGR8->pixels(iy); |
| for (int ix = 0; ix < m_imageWidth; ix++) { |
| tmpPix->m = maskPix->value; |
| tmpPix++; |
| maskPix++; |
| } |
| } |
| |
| if (m_preMolt->getValue()) TRop::premultiply(ras32); |
| stop_time = clock(); |
| double durata = (double)(stop_time - start_time) / CLOCKS_PER_SEC; |
| |
| TSystem::outputDebug( |
| "\n#Pixel:\t" + toString(m_imageWidth * m_imageHeigth) + "\nProc:\t" + |
| toString(pixelProcessed) + "\t[" + |
| toString((pixelProcessed * 100 / (m_imageWidth * m_imageHeigth))) + |
| "%t]" + "\nMask:\t" + toString(pixelMasked) + "\t[" + |
| toString((pixelMasked * 100 / (m_imageWidth * m_imageHeigth))) + "%t]" + |
| "\t[" + toString((pixelMasked * 100 / (pixelProcessed))) + "%p]" + |
| "\nEnqu:\t" + toString(shadowEnqueued) + "\nRepr:\t" + |
| toString(pixelReprocessed) + "\t[" + |
| toString((pixelReprocessed * 100 / (m_imageWidth * m_imageHeigth))) + |
| "%t]" + "\t[" + toString((pixelReprocessed * 100 / (pixelProcessed))) + |
| "%p]" + "\nOutB:\t" + toString(shadowOutOfBorder) + "\t[" + |
| toString((shadowOutOfBorder * 100 / (shadowEnqueued))) + "%t]" + |
| "\nTime:\t" + toString(durata, 3) + " sec\n[MagicWandFX <end>]\n"); |
| |
| } |
| else { |
| TRasterGR8P rasGR8 = tile.getRaster(); |
| if (rasGR8) { |
| } |
| } |
| tile.getRaster()->unlock(); |
| m_maskGR8->unlock(); |
| } |
| |
| void MagicWandFx::doCompute(TTile &tile, double frame, |
| const TRasterFxRenderInfo *ri) { |
| if (!m_input.isConnected()) return; |
| |
| m_input->compute(tile, frame, ri); |
| |
| doMagicWand(tile, frame, ri); |
| } |
| |
| |
| |
| TRect MagicWandFx::getInvalidRect(const TRect &max) { return max; } |
| |
| |
| |
| FX_PLUGIN_IDENTIFIER(MagicWandFx, magicWandFx); |
| |