| |
| |
|
|
| #include "tstream.h" |
| #include "trop.h" |
| #include "tflash.h" |
| |
| |
| #include "tdoubleparam.h" |
| #include "tnotanimatableparam.h" |
| #include "tfxparam.h" |
| #include "trasterfx.h" |
| #include "tbasefx.h" |
| |
| |
| |
| |
| |
| namespace { |
| void makeRectCoherent(TRectD &rect, const TPointD &pos) { |
| rect -= pos; |
| rect.x0 = tfloor(rect.x0); |
| rect.y0 = tfloor(rect.y0); |
| rect.x1 = tceil(rect.x1); |
| rect.y1 = tceil(rect.y1); |
| rect += pos; |
| } |
| } |
| |
| |
| |
| |
| |
| class TImageCombinationFx : public TBaseRasterFx { |
| TFxPortDG m_group; |
| |
| public: |
| TImageCombinationFx(); |
| virtual ~TImageCombinationFx() {} |
| |
| public: |
| |
| |
| |
| |
| virtual void process(const TRasterP &up, const TRasterP &down, |
| double frame) = 0; |
| |
| |
| |
| |
| virtual bool requiresFullRect() { return false; } |
| |
| public: |
| |
| |
| int dynamicPortGroupsCount() const { return 1; } |
| const TFxPortDG *dynamicPortGroup(int g) const { |
| return (g == 0) ? &m_group : 0; |
| } |
| |
| bool canHandle(const TRenderSettings &info, double frame) { return true; } |
| |
| int getMemoryRequirement(const TRectD &rect, double frame, |
| const TRenderSettings &info) { |
| |
| |
| |
| return TRasterFx::memorySize(rect, info.m_bpp); |
| } |
| |
| bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info); |
| |
| void doDryCompute(TRectD &rect, double frame, const TRenderSettings &info); |
| void doCompute(TTile &tile, double frame, const TRenderSettings &info); |
| |
| void compatibilityTranslatePort(int majorVersion, int minorVersion, |
| std::string &portName); |
| |
| int getPreferredInputPort() { return 1; } |
| }; |
| |
| |
| |
| |
| |
| TImageCombinationFx::TImageCombinationFx() : m_group("Source", 2) { |
| addInputPort("Source1", new TRasterFxPort, 0); |
| addInputPort("Source2", new TRasterFxPort, 0); |
| setName(L"ImageCombinationFx"); |
| } |
| |
| |
| |
| bool TImageCombinationFx::doGetBBox(double frame, TRectD &bBox, |
| const TRenderSettings &info) { |
| bBox = TRectD(); |
| |
| int p, pCount = getInputPortCount(); |
| for (p = 0; p != pCount; ++p) { |
| TRasterFxPort *port = static_cast<TRasterFxPort *>(getInputPort(p)); |
| TRectD inputBBox; |
| |
| bool hasInput = (port && port->isConnected()) |
| ? (*port)->doGetBBox(frame, inputBBox, info) |
| : false; |
| |
| if (hasInput) bBox += inputBBox; |
| } |
| |
| return (bBox.getLx() >= 0) && (bBox.getLy() >= 0); |
| } |
| |
| |
| |
| void TImageCombinationFx::doCompute(TTile &tile, double frame, |
| const TRenderSettings &info) { |
| int p, pCount = getInputPortCount(); |
| TRasterFxPort *port = 0; |
| |
| |
| for (p = pCount - 1; p >= 0; |
| --p) |
| { |
| port = static_cast<TRasterFxPort *>(getInputPort(p)); |
| if (port && port->getFx()) break; |
| } |
| |
| |
| if (p < 0) { |
| tile.getRaster() |
| ->clear(); |
| return; |
| } |
| |
| |
| const TRect &tileRect(tile.getRaster()->getBounds()); |
| const TDimension &tileSize(tileRect.getSize()); |
| TRectD tileRectD(tile.m_pos, TDimensionD(tileSize.lx, tileSize.ly)); |
| |
| |
| (*port)->compute(tile, frame, |
| info); |
| |
| |
| bool canRestrict = !requiresFullRect(); |
| |
| for (--p; p >= 0; --p) { |
| port = static_cast<TRasterFxPort *>(getInputPort(p)); |
| if (!(port && port->getFx())) |
| continue; |
| |
| |
| |
| |
| TRectD computeRect(tileRectD); |
| |
| if (canRestrict) { |
| TRectD inBBox; |
| (*port)->getBBox(frame, inBBox, info); |
| |
| computeRect *= inBBox; |
| |
| makeRectCoherent( |
| computeRect, |
| tile.m_pos); |
| } |
| |
| |
| TDimension computeSize(tround(computeRect.getLx()), |
| tround(computeRect.getLy())); |
| if ((computeSize.lx > 0) && (computeSize.ly > 0)) { |
| TTile inTile; |
| (*port)->allocateAndCompute(inTile, computeRect.getP00(), computeSize, |
| tile.getRaster(), frame, info); |
| |
| |
| TRasterP up(inTile.getRaster()), down(tile.getRaster()); |
| |
| if (canRestrict) { |
| |
| TRect downRect(convert(computeRect.getP00() - tile.m_pos), computeSize); |
| down = down->extract(downRect); |
| } |
| |
| assert(up->getSize() == down->getSize()); |
| process(up, down, frame); |
| |
| } |
| } |
| } |
| |
| |
| |
| void TImageCombinationFx::doDryCompute(TRectD &rect, double frame, |
| const TRenderSettings &info) { |
| |
| |
| int p, pCount = getInputPortCount(); |
| TRasterFxPort *port = 0; |
| |
| for (p = pCount - 1; p >= 0; --p) { |
| port = static_cast<TRasterFxPort *>(getInputPort(p)); |
| if (port && port->getFx()) break; |
| } |
| |
| if (p < 0) return; |
| |
| (*port)->dryCompute(rect, frame, info); |
| |
| bool canRestrict = !requiresFullRect(); |
| |
| for (--p; p >= 0; --p) { |
| port = static_cast<TRasterFxPort *>(getInputPort(p)); |
| if (!(port && port->getFx())) continue; |
| |
| TRectD computeRect(rect); |
| |
| if (canRestrict) { |
| TRectD inBBox; |
| (*port)->getBBox(frame, inBBox, info); |
| |
| computeRect *= inBBox; |
| makeRectCoherent(computeRect, rect.getP00()); |
| } |
| |
| TDimension computeSize(tround(computeRect.getLx()), |
| tround(computeRect.getLy())); |
| if ((computeSize.lx > 0) && (computeSize.ly > 0)) |
| (*port)->dryCompute(computeRect, frame, info); |
| } |
| } |
| |
| |
| |
| void TImageCombinationFx::compatibilityTranslatePort(int major, int minor, |
| std::string &portName) { |
| if (VersionNumber(major, minor) < VersionNumber(1, 20)) { |
| if (portName == "Up") |
| portName = "Source1"; |
| else if (portName == "Down") |
| portName = "Source2"; |
| } |
| } |
| |
| |
| |
| |
| |
| class OverFx : public TImageCombinationFx { |
| FX_DECLARATION(OverFx) |
| |
| public: |
| OverFx() { setName(L"OverFx"); } |
| |
| void process(const TRasterP &up, const TRasterP &down, double frame) { |
| TRop::over(down, up); |
| } |
| }; |
| |
| |
| |
| class AddFx : public TImageCombinationFx { |
| FX_DECLARATION(AddFx) |
| |
| TDoubleParamP m_value; |
| |
| public: |
| AddFx() : m_value(100.0) { bindParam(this, "value", m_value); } |
| |
| void process(const TRasterP &up, const TRasterP &down, double frame) { |
| double value = m_value->getValue(frame) / 100.0; |
| |
| if (value != 1.0) |
| TRop::add(up, down, down, value); |
| else |
| TRop::add(up, down, down); |
| } |
| |
| TFxPort *getXsheetPort() const { return getInputPort(1); } |
| }; |
| |
| |
| |
| class ColorDodgeFx : public TImageCombinationFx { |
| FX_DECLARATION(AddFx) |
| |
| public: |
| void process(const TRasterP &up, const TRasterP &down, double frame) { |
| TRop::colordodge(up, down, down); |
| } |
| |
| TFxPort *getXsheetPort() const { return getInputPort(1); } |
| }; |
| |
| |
| |
| class ColorBurnFx : public TImageCombinationFx { |
| FX_DECLARATION(AddFx) |
| |
| public: |
| void process(const TRasterP &up, const TRasterP &down, double frame) { |
| TRop::colorburn(up, down, down); |
| } |
| |
| TFxPort *getXsheetPort() const { return getInputPort(1); } |
| }; |
| |
| |
| |
| class ScreenFx : public TImageCombinationFx { |
| FX_DECLARATION(AddFx) |
| |
| public: |
| bool requiresFullRect() { return true; } |
| |
| void process(const TRasterP &up, const TRasterP &down, double frame) { |
| TRop::screen(up, down, down); |
| } |
| |
| TFxPort *getXsheetPort() const { return getInputPort(1); } |
| }; |
| |
| |
| |
| class SubFx : public TImageCombinationFx { |
| FX_DECLARATION(SubFx) |
| |
| TBoolParamP m_matte; |
| |
| public: |
| SubFx() : m_matte(false) { bindParam(this, "matte", m_matte); } |
| |
| void process(const TRasterP &up, const TRasterP &down, double frame) { |
| TRop::sub(up, down, down, m_matte->getValue()); |
| } |
| |
| TFxPort *getXsheetPort() const { return getInputPort(1); } |
| }; |
| |
| |
| |
| class MultFx : public TImageCombinationFx { |
| FX_DECLARATION(MultFx) |
| |
| TDoubleParamP m_value; |
| TBoolParamP m_matte; |
| |
| public: |
| MultFx() : m_value(0.0), m_matte(false) { |
| bindParam(this, "value", m_value); |
| bindParam(this, "matte", m_matte); |
| } |
| |
| bool requiresFullRect() { return m_matte->getValue(); } |
| |
| void process(const TRasterP &up, const TRasterP &down, double frame) { |
| TRop::mult(up, down, down, m_value->getValue(frame), m_matte->getValue()); |
| } |
| |
| TFxPort *getXsheetPort() const { return getInputPort(1); } |
| }; |
| |
| |
| |
| class MinFx : public TImageCombinationFx { |
| FX_DECLARATION(MinFx) |
| |
| TBoolParamP m_matte; |
| |
| public: |
| MinFx() : m_matte(true) { bindParam(this, "matte", m_matte); } |
| |
| bool requiresFullRect() { return true; } |
| |
| void process(const TRasterP &up, const TRasterP &down, double frame) { |
| TRop::ropmin(up, down, down, m_matte->getValue()); |
| } |
| |
| TFxPort *getXsheetPort() const { return getInputPort(1); } |
| }; |
| |
| |
| |
| class MaxFx : public TImageCombinationFx { |
| FX_DECLARATION(MaxFx) |
| |
| public: |
| void process(const TRasterP &up, const TRasterP &down, double frame) { |
| TRop::ropmax(up, down, down); |
| } |
| |
| TFxPort *getXsheetPort() const { return getInputPort(1); } |
| }; |
| |
| |
| |
| class LinearBurnFx : public TImageCombinationFx { |
| FX_DECLARATION(LinearBurnFx) |
| |
| public: |
| void process(const TRasterP &up, const TRasterP &down, double frame) { |
| TRop::linearburn(up, down, down); |
| } |
| |
| TFxPort *getXsheetPort() const { return getInputPort(1); } |
| }; |
| |
| |
| |
| |
| class OverlayFx : public TImageCombinationFx { |
| FX_DECLARATION(OverlayFx) |
| |
| public: |
| OverlayFx() {} |
| ~OverlayFx() {} |
| |
| void process(const TRasterP &up, const TRasterP &down, double frame) { |
| TRop::overlay(up, down, down); |
| } |
| }; |
| |
| |
| |
| class BlendFx : public TImageCombinationFx { |
| FX_DECLARATION(BlendFx) |
| |
| TDoubleParamP m_value; |
| |
| public: |
| BlendFx() : m_value(0.0) { |
| bindParam(this, "value", m_value); |
| m_value->setValueRange(0.0, 100.0); |
| } |
| |
| bool requiresFullRect() { return true; } |
| |
| void process(const TRasterP &up, const TRasterP &down, double frame) { |
| double value = 0.01 * m_value->getValue(frame); |
| UCHAR matteValue = (UCHAR)(value * 255.0 + 0.5); |
| |
| TRop::crossDissolve(up, down, down, matteValue); |
| } |
| |
| TFxPort *getXsheetPort() const { return getInputPort(1); } |
| }; |
| |
| |
| |
| |
| |
| class InFx : public TBaseRasterFx { |
| FX_DECLARATION(InFx) |
| |
| TRasterFxPort m_source, m_matte; |
| |
| public: |
| InFx() { |
| addInputPort("Source", m_source); |
| addInputPort("Matte", m_matte); |
| setName(L"InFx"); |
| } |
| |
| ~InFx() {} |
| |
| bool doGetBBox(double frame, TRectD &bbox, const TRenderSettings &info) { |
| if (m_matte.isConnected() && m_source.isConnected()) { |
| bool ret = m_matte->doGetBBox(frame, bbox, info); |
| |
| if (bbox == TConsts::infiniteRectD) |
| return m_source->doGetBBox(frame, bbox, info); |
| else |
| return ret; |
| } |
| bbox = TRectD(); |
| return false; |
| } |
| |
| bool canHandle(const TRenderSettings &info, double frame) { return true; } |
| |
| void doCompute(TTile &tile, double frame, const TRenderSettings &ri) { |
| |
| |
| if (!(m_source.isConnected() && m_matte.isConnected())) return; |
| |
| TTile srcTile; |
| m_source->allocateAndCompute(srcTile, tile.m_pos, |
| tile.getRaster()->getSize(), tile.getRaster(), |
| frame, ri); |
| |
| m_matte->compute(tile, frame, ri); |
| |
| TRop::ropin(srcTile.getRaster(), tile.getRaster(), tile.getRaster()); |
| } |
| |
| void doDryCompute(TRectD &rect, double frame, const TRenderSettings &info) { |
| if (!(m_source.isConnected() && m_matte.isConnected())) return; |
| |
| m_source->dryCompute(rect, frame, info); |
| m_matte->dryCompute(rect, frame, info); |
| } |
| |
| int getMemoryRequirement(const TRectD &rect, double frame, |
| const TRenderSettings &info) { |
| return TRasterFx::memorySize(rect, info.m_bpp); |
| } |
| |
| void compute(TFlash &flash, int frame) { |
| if (m_matte.isConnected()) { |
| flash.pushMatrix(); |
| flash.beginMask(); |
| ((TRasterFxP)(m_matte.getFx()))->compute(flash, frame); |
| flash.endMask(); |
| flash.popMatrix(); |
| } |
| |
| if (m_source.isConnected()) { |
| flash.pushMatrix(); |
| flash.enableMask(); |
| ((TRasterFxP)(m_source.getFx()))->compute(flash, frame); |
| flash.disableMask(); |
| flash.popMatrix(); |
| } |
| } |
| }; |
| |
| |
| |
| class OutFx : public TBaseRasterFx { |
| FX_DECLARATION(OutFx) |
| |
| TRasterFxPort m_source, m_matte; |
| |
| public: |
| OutFx() { |
| addInputPort("Source", m_source); |
| addInputPort("Matte", m_matte); |
| setName(L"OutFx"); |
| } |
| |
| ~OutFx() {} |
| |
| bool doGetBBox(double frame, TRectD &bbox, const TRenderSettings &info) { |
| if (m_source.isConnected()) return m_source->doGetBBox(frame, bbox, info); |
| |
| return false; |
| } |
| |
| bool canHandle(const TRenderSettings &info, double frame) { return true; } |
| |
| void doCompute(TTile &tile, double frame, const TRenderSettings &ri) { |
| |
| if (!m_source.isConnected()) return; |
| |
| |
| |
| if (!m_matte.isConnected()) { |
| m_source->compute(tile, frame, ri); |
| return; |
| } |
| |
| TTile srcTile; |
| m_source->allocateAndCompute(srcTile, tile.m_pos, |
| tile.getRaster()->getSize(), tile.getRaster(), |
| frame, ri); |
| |
| m_matte->compute(tile, frame, ri); |
| |
| TRop::ropout(srcTile.getRaster(), tile.getRaster(), tile.getRaster()); |
| } |
| |
| void doDryCompute(TRectD &rect, double frame, const TRenderSettings &info) { |
| if (!m_source.isConnected()) return; |
| |
| if (!m_matte.isConnected()) { |
| m_source->dryCompute(rect, frame, info); |
| return; |
| } |
| |
| m_source->dryCompute(rect, frame, info); |
| m_matte->dryCompute(rect, frame, info); |
| } |
| |
| int getMemoryRequirement(const TRectD &rect, double frame, |
| const TRenderSettings &info) { |
| return TRasterFx::memorySize(rect, info.m_bpp); |
| } |
| }; |
| |
| |
| |
| class AtopFx : public TBaseRasterFx { |
| FX_DECLARATION(AtopFx) |
| |
| TRasterFxPort m_up, m_dn; |
| |
| public: |
| AtopFx() { |
| addInputPort("Up", m_up); |
| addInputPort("Down", m_dn); |
| } |
| |
| bool canHandle(const TRenderSettings &info, double frame) { return true; } |
| |
| bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) { |
| bBox = TRectD(); |
| |
| { |
| TRectD inputBBox; |
| |
| bool hasInput = |
| m_up.isConnected() ? m_up->doGetBBox(frame, inputBBox, info) : false; |
| if (hasInput) bBox += inputBBox; |
| } |
| |
| { |
| TRectD inputBBox; |
| |
| bool hasInput = |
| m_dn.isConnected() ? m_dn->doGetBBox(frame, inputBBox, info) : false; |
| if (hasInput) bBox += inputBBox; |
| } |
| |
| return (bBox.getLx() >= 0) && (bBox.getLy() >= 0); |
| } |
| |
| void doCompute(TTile &tile, double frame, const TRenderSettings &ri) { |
| |
| |
| if (!m_dn.isConnected()) return; |
| |
| if (!m_up.isConnected()) { |
| m_dn->compute(tile, frame, ri); |
| return; |
| } |
| |
| TTile upTile; |
| m_up->allocateAndCompute(upTile, tile.m_pos, tile.getRaster()->getSize(), |
| tile.getRaster(), frame, ri); |
| |
| m_dn->compute(tile, frame, ri); |
| |
| TRop::atop(upTile.getRaster(), tile.getRaster(), tile.getRaster()); |
| } |
| |
| void doDryCompute(TRectD &rect, double frame, const TRenderSettings &info) { |
| if (!m_dn.isConnected()) return; |
| |
| if (!m_up.isConnected()) { |
| m_dn->dryCompute(rect, frame, info); |
| return; |
| } |
| |
| m_up->dryCompute(rect, frame, info); |
| m_dn->dryCompute(rect, frame, info); |
| } |
| |
| int getMemoryRequirement(const TRectD &rect, double frame, |
| const TRenderSettings &info) { |
| return TRasterFx::memorySize(rect, info.m_bpp); |
| } |
| }; |
| |
| |
| |
| |
| |
| |
| |
| FX_IDENTIFIER(OverFx, "overFx") |
| FX_IDENTIFIER(AddFx, "addFx") |
| FX_IDENTIFIER(SubFx, "subFx") |
| FX_IDENTIFIER(MultFx, "multFx") |
| FX_IDENTIFIER(InFx, "inFx") |
| FX_IDENTIFIER(OutFx, "outFx") |
| FX_IDENTIFIER(AtopFx, "atopFx") |
| |
| FX_IDENTIFIER(MinFx, "minFx") |
| FX_IDENTIFIER(MaxFx, "maxFx") |
| FX_IDENTIFIER(LinearBurnFx, "linearBurnFx") |
| FX_IDENTIFIER(OverlayFx, "overlayFx") |
| FX_IDENTIFIER(BlendFx, "blendFx") |
| FX_IDENTIFIER(ColorDodgeFx, "colorDodgeFx") |
| FX_IDENTIFIER(ColorBurnFx, "colorBurnFx") |
| FX_IDENTIFIER(ScreenFx, "screenFx") |
| |