| |
| |
| #include "trasterfx.h" |
| |
| |
| #include "tsystem.h" |
| #include "tthreadmessage.h" |
| |
| |
| #include "tparamcontainer.h" |
| #include "tbasefx.h" |
| #include "tfxattributes.h" |
| |
| |
| #include "timagecache.h" |
| #include "trasterimage.h" |
| #include "trop.h" |
| |
| |
| #include "trenderresourcemanager.h" |
| #include "tfxcachemanager.h" |
| #include "trenderer.h" |
| |
| |
| |
| #ifdef DIAGNOSTICS |
| #include "diagnostics.h" |
| |
| #endif // DIAGNOSTICS |
| |
| |
| |
| namespace { |
| inline bool areEqual(const TRasterFxRenderDataP &d1, |
| const TRasterFxRenderDataP &d2) { |
| return (*d1 == *d2); |
| } |
| |
| |
| |
| inline int myCeil(double x) { |
| return ((x - (int)(x)) > TConsts::epsilon ? (int)(x) + 1 : (int)(x)); |
| } |
| |
| |
| |
| inline TRect myConvert(const TRectD &r, TPointD &dp) { |
| TRect ri(tfloor(r.x0), tfloor(r.y0), myCeil(r.x1), myCeil(r.y1)); |
| dp.x = r.x0 - ri.x0; |
| dp.y = r.y0 - ri.y0; |
| assert(dp.x >= 0 && dp.y >= 0); |
| return ri; |
| } |
| |
| |
| |
| inline bool myIsEmpty(const TRectD &r) { |
| return r.getLx() < 1 || r.getLy() < 1; |
| } |
| |
| |
| |
| inline TRect myConvert(const TRectD &rect) { |
| return TRect(tround(rect.x0), tround(rect.y0), tround(rect.x1) - 1, |
| tround(rect.y1) - 1); |
| } |
| |
| |
| |
| inline TRectD myConvert(const TRect &rect) { |
| return TRectD(rect.x0, rect.y0, rect.x1 + 1, rect.y1 + 1); |
| } |
| |
| |
| |
| inline void enlargeToI(TRectD &r) { |
| TRectD temp(tfloor(r.x0), tfloor(r.y0), tceil(r.x1), tceil(r.y1)); |
| |
| |
| if (!myIsEmpty(temp)) r = temp; |
| } |
| |
| |
| |
| |
| |
| double norm2(const TAffine &aff) { |
| double a11 = aff.a11 * aff.a11 + aff.a12 * aff.a12; |
| double a12 = aff.a11 * aff.a21 + aff.a12 * aff.a22; |
| double a21 = aff.a21 * aff.a11 + aff.a22 * aff.a12; |
| double a22 = aff.a21 * aff.a21 + aff.a22 * aff.a22; |
| |
| double a11plusa22 = a11 + a22; |
| double delta = sq(a11 - a22) + 4 * a12 * a21; |
| |
| delta = sqrt(delta); |
| |
| double eig1 = a11plusa22 + delta; |
| double eig2 = a11plusa22 - delta; |
| return std::max(sqrt(eig1 / 2.0), sqrt(eig2 / 2.0)); |
| } |
| |
| |
| |
| inline int getResampleFilterRadius(const TRenderSettings &info) { |
| switch (info.m_quality) { |
| case TRenderSettings::StandardResampleQuality: |
| return 1; |
| case TRenderSettings::ImprovedResampleQuality: |
| return 2; |
| case TRenderSettings::HighResampleQuality: |
| return 3; |
| case TRenderSettings::Triangle_FilterResampleQuality: |
| return 1; |
| case TRenderSettings::Mitchell_FilterResampleQuality: |
| return 2; |
| case TRenderSettings::Cubic5_FilterResampleQuality: |
| return 2; |
| case TRenderSettings::Cubic75_FilterResampleQuality: |
| return 2; |
| case TRenderSettings::Cubic1_FilterResampleQuality: |
| return 2; |
| case TRenderSettings::Hann2_FilterResampleQuality: |
| return 2; |
| case TRenderSettings::Hann3_FilterResampleQuality: |
| return 3; |
| case TRenderSettings::Hamming2_FilterResampleQuality: |
| return 2; |
| case TRenderSettings::Hamming3_FilterResampleQuality: |
| return 3; |
| case TRenderSettings::Lanczos2_FilterResampleQuality: |
| return 2; |
| case TRenderSettings::Lanczos3_FilterResampleQuality: |
| return 3; |
| case TRenderSettings::Gauss_FilterResampleQuality: |
| return 2; |
| case TRenderSettings::ClosestPixel_FilterResampleQuality: |
| return 1; |
| case TRenderSettings::Bilinear_FilterResampleQuality: |
| return 1; |
| default: |
| assert(false); |
| return -1; |
| } |
| } |
| |
| |
| |
| inline QString traduce(const TRectD &rect) { |
| return "[" + QString::number(rect.x0) + " " + QString::number(rect.y0) + " " + |
| QString::number(rect.x1) + " " + QString::number(rect.y1) + "]"; |
| } |
| |
| |
| |
| inline QString traduce(const TRect &rect) { |
| return "[" + QString::number(rect.x0) + " " + QString::number(rect.y0) + " " + |
| QString::number(rect.x1) + " " + QString::number(rect.y1) + "]"; |
| } |
| |
| |
| |
| inline QString qTraduce(const TAffine &aff) { |
| return "[" + QString::number(aff.a11, 'g', 15) + "," + |
| QString::number(aff.a12, 'g', 15) + "," + |
| QString::number(aff.a13, 'g', 15) + "," + |
| QString::number(aff.a21, 'g', 15) + "," + |
| QString::number(aff.a22, 'g', 15) + "," + |
| QString::number(aff.a23, 'g', 15) + "]"; |
| } |
| |
| |
| |
| inline std::string traduce(const TAffine &aff) { |
| return |
| |
| |
| (areAlmostEqual(aff.a11, 0.0) ? "0" : ::to_string(aff.a11, 5)) + "," + |
| (areAlmostEqual(aff.a12, 0.0) ? "0" : ::to_string(aff.a12, 5)) + "," + |
| (areAlmostEqual(aff.a13, 0.0) ? "0" : ::to_string(aff.a13, 5)) + "," + |
| (areAlmostEqual(aff.a21, 0.0) ? "0" : ::to_string(aff.a21, 5)) + "," + |
| (areAlmostEqual(aff.a22, 0.0) ? "0" : ::to_string(aff.a22, 5)) + "," + |
| (areAlmostEqual(aff.a23, 0.0) ? "0" : ::to_string(aff.a23, 5)); |
| } |
| |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| void addRenderCache(const std::string &alias, TImageP image) { |
| TFxCacheManager::instance()->add(alias, image); |
| } |
| |
| |
| |
| |
| void removeRenderCache(const std::string &alias) { |
| TFxCacheManager::instance()->remove(alias); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| class TrFx final : public TBaseRasterFx { |
| FX_DECLARATION(TrFx) |
| |
| TRasterFx *m_fx; |
| |
| public: |
| TrFx() {} |
| ~TrFx() {} |
| |
| |
| |
| void setFx(TRasterFx *fx) { m_fx = fx; } |
| |
| |
| |
| bool isCachable() const override { |
| return true; |
| } |
| |
| |
| |
| bool canHandle(const TRenderSettings &info, double frame) override { |
| return true; |
| } |
| |
| |
| |
| std::string getAlias(double frame, |
| const TRenderSettings &info) const override { |
| |
| |
| |
| std::string alias = getFxType(); |
| return alias + "[" + m_fx->getAlias(frame, info) + "]"; |
| } |
| |
| |
| |
| bool doGetBBox(double frame, TRectD &bBox, |
| const TRenderSettings &info) override { |
| |
| |
| return m_fx->doGetBBox(frame, bBox, info); |
| } |
| |
| |
| |
| void doCompute(TTile &tile, double frame, |
| const TRenderSettings &info) override { |
| const TRectD &rectOut = |
| myConvert(tile.getRaster()->getBounds()) + tile.m_pos; |
| |
| TRectD rectIn; |
| TRenderSettings infoIn(info); |
| TAffine appliedAff; |
| |
| if (!buildInput(rectOut, frame, info, rectIn, infoIn, appliedAff)) return; |
| |
| const TRect &rectInI = myConvert(rectIn); |
| |
| |
| |
| TTile inTile; |
| m_fx->allocateAndCompute(inTile, rectIn.getP00(), |
| TDimension(rectInI.getLx(), rectInI.getLy()), |
| tile.getRaster(), frame, infoIn); |
| |
| infoIn.m_affine = appliedAff; |
| TRasterFx::applyAffine(tile, inTile, infoIn); |
| } |
| |
| |
| |
| void doDryCompute(TRectD &rect, double frame, |
| const TRenderSettings &info) override { |
| TRectD rectIn; |
| TRenderSettings infoIn(info); |
| TAffine appliedAff; |
| |
| if (!buildInput(rect, frame, info, rectIn, infoIn, appliedAff)) return; |
| |
| m_fx->dryCompute(rectIn, frame, infoIn); |
| } |
| |
| |
| |
| int getMemoryRequirement(const TRectD &rect, double frame, |
| const TRenderSettings &info) override { |
| TRectD rectIn; |
| TRenderSettings infoIn(info); |
| TAffine appliedAff; |
| |
| if (!buildInput(rect, frame, info, rectIn, infoIn, appliedAff)) return 0; |
| |
| return TRasterFx::memorySize(rectIn, info.m_bpp); |
| } |
| |
| private: |
| bool buildInput(const TRectD &rectOut, double frame, |
| const TRenderSettings &infoOut, TRectD &rectIn, |
| TRenderSettings &infoIn, TAffine &appliedAff) { |
| if (myIsEmpty(rectOut)) return false; |
| |
| |
| infoIn.m_affine = m_fx->handledAffine(infoOut, frame); |
| appliedAff = infoOut.m_affine * infoIn.m_affine.inv(); |
| |
| |
| |
| |
| |
| |
| if (fabs(appliedAff.det()) < 1e-6) |
| return false; |
| |
| |
| const TAffine &appliedAffInv = appliedAff.inv(); |
| int filterRadius = getResampleFilterRadius(infoOut); |
| |
| TRectD bbox; |
| m_fx->getBBox(frame, bbox, infoIn); |
| |
| rectIn = |
| ((appliedAffInv * rectOut) |
| .enlarge( |
| filterRadius) + |
| (appliedAffInv * rectOut.enlarge(filterRadius))) * |
| bbox; |
| |
| if (myIsEmpty(rectIn)) return false; |
| |
| enlargeToI(rectIn); |
| return true; |
| } |
| }; |
| |
| FX_IDENTIFIER_IS_HIDDEN(TrFx, "trFx") |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| class FxResourceBuilder final : public ResourceBuilder { |
| TRasterFxP m_rfx; |
| double m_frame; |
| const TRenderSettings *m_rs; |
| |
| TTile *m_outTile; |
| TTile *m_currTile; |
| TTile m_newTile; |
| |
| TRectD m_outRect; |
| |
| public: |
| FxResourceBuilder(const std::string &resourceName, const TRasterFxP &fx, |
| const TRenderSettings &rs, double frame) |
| : ResourceBuilder(resourceName, fx.getPointer(), frame, rs) |
| , m_rfx(fx) |
| , m_frame(frame) |
| , m_rs(&rs) |
| , m_currTile(0) {} |
| |
| inline void build(TTile &tile); |
| |
| protected: |
| void simCompute(const TRectD &rect) override { |
| TRectD rectCpy( |
| rect); |
| m_rfx->doDryCompute(rectCpy, m_frame, *m_rs); |
| } |
| |
| void buildTileToCalculate(const TRectD &tileRect); |
| void compute(const TRectD &tileRect) override; |
| |
| void upload(TCacheResourceP &resource) override; |
| bool download(TCacheResourceP &resource) override; |
| }; |
| |
| |
| |
| inline void FxResourceBuilder::build(TTile &tile) { |
| m_outTile = &tile; |
| |
| TDimension dim(tile.getRaster()->getSize()); |
| m_outRect = TRectD(tile.m_pos, TDimensionD(dim.lx, dim.ly)); |
| |
| ResourceBuilder::build(m_outRect); |
| } |
| |
| |
| |
| void FxResourceBuilder::buildTileToCalculate(const TRectD &tileGeom) { |
| if (tileGeom == m_outRect) { |
| m_currTile = m_outTile; |
| return; |
| } |
| |
| m_newTile.m_pos = tileGeom.getP00(); |
| |
| TRasterP outRas(m_outTile->getRaster()); |
| |
| |
| |
| TDimension outputSize(outRas->getSize()); |
| TDimension requiredSize(tceil(tileGeom.getLx()), tceil(tileGeom.getLy())); |
| |
| TRasterP ras; |
| if (outputSize.lx >= requiredSize.lx && outputSize.ly >= requiredSize.ly) { |
| |
| TRect rect(0, 0, requiredSize.lx - 1, requiredSize.ly - 1); |
| ras = outRas->extract(rect); |
| ras->clear(); |
| } else |
| ras = outRas->create(requiredSize.lx, requiredSize.ly); |
| |
| m_newTile.setRaster(ras); |
| |
| m_currTile = &m_newTile; |
| } |
| |
| |
| |
| void FxResourceBuilder::compute(const TRectD &tileRect) { |
| #ifdef DIAGNOSTICS |
| TStopWatch sw; |
| sw.start(); |
| #endif |
| |
| buildTileToCalculate(tileRect); |
| m_rfx->doCompute(*m_currTile, m_frame, *m_rs); |
| |
| #ifdef DIAGNOSTICS |
| sw.stop(); |
| |
| DIAGNOSTICS_THRSET("FComputeTime", sw.getTotalTime()); |
| #endif |
| } |
| |
| |
| |
| void FxResourceBuilder::upload(TCacheResourceP &resource) { |
| resource->upload(*m_currTile); |
| if (m_currTile == &m_newTile) m_newTile.setRaster(0); |
| } |
| |
| |
| |
| bool FxResourceBuilder::download(TCacheResourceP &resource) { |
| |
| if (m_currTile == m_outTile) return true; |
| |
| return resource->downloadAll(*m_outTile); |
| } |
| |
| |
| |
| |
| |
| |
| |
| class TRasterFx::TRasterFxImp { |
| public: |
| bool m_cacheEnabled; |
| TTile m_cachedTile; |
| double m_frame; |
| bool m_isEnabled; |
| |
| TRenderSettings m_info; |
| std::string m_interactiveCacheId; |
| mutable TThread::Mutex m_mutex; |
| |
| TRasterFxImp() : m_cacheEnabled(false), m_isEnabled(true), m_cachedTile(0) {} |
| |
| ~TRasterFxImp() {} |
| |
| void enableCache(bool on) { |
| QMutexLocker sl(&m_mutex); |
| m_cacheEnabled = on; |
| if (!m_cacheEnabled) { |
| m_interactiveCacheId = ""; |
| m_frame = 0; |
| m_info = TRenderSettings(); |
| m_cachedTile.setRaster(0); |
| m_cachedTile.m_pos = TPointD(); |
| } |
| } |
| |
| bool isCacheEnabled() const { return m_cacheEnabled; } |
| |
| bool isEnabled() const { |
| QMutexLocker sl(&m_mutex); |
| return m_isEnabled; |
| } |
| |
| void enable(bool on) { |
| QMutexLocker sl(&m_mutex); |
| m_isEnabled = on; |
| } |
| }; |
| |
| |
| |
| TRasterFx::TRasterFx() : m_rasFxImp(new TRasterFxImp) {} |
| |
| |
| |
| TRasterFx::~TRasterFx() { delete m_rasFxImp; } |
| |
| |
| |
| TAffine TRasterFx::handledAffine(const TRenderSettings &info, double frame) { |
| return (info.m_affine.a11 == info.m_affine.a22 && info.m_affine.a12 == 0 && |
| info.m_affine.a21 == 0 && info.m_affine.a13 == 0 && |
| info.m_affine.a23 == 0) |
| ? info.m_affine |
| : TScale(norm2(info.m_affine)); |
| } |
| |
| |
| |
| bool TRasterFx::getBBox(double frame, TRectD &bBox, |
| const TRenderSettings &info) { |
| bool ret = doGetBBox(frame, bBox, info); |
| bBox = info.m_affine * bBox; |
| enlargeToI(bBox); |
| return ret; |
| } |
| |
| |
| |
| void TRasterFx::transform(double frame, int port, const TRectD &rectOnOutput, |
| const TRenderSettings &infoOnOutput, |
| TRectD &rectOnInput, TRenderSettings &infoOnInput) { |
| rectOnInput = rectOnOutput; |
| infoOnInput = infoOnOutput; |
| } |
| |
| |
| |
| int TRasterFx::memorySize(const TRectD &rect, int bpp) { |
| if (rect.x1 <= rect.x0 || rect.y1 <= rect.y0) return 0; |
| |
| return (unsigned long)(rect.getLx() + 1) * (unsigned long)(rect.getLy() + 1) * |
| (bpp >> 3) >> |
| 20; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int TRasterFx::getMemoryRequirement(const TRectD &rect, double frame, |
| const TRenderSettings &info) { |
| return 0; |
| } |
| |
| |
| |
| std::string TRasterFx::getAlias(double frame, |
| const TRenderSettings &info) const { |
| std::string alias = getFxType(); |
| alias += "["; |
| |
| |
| |
| int i; |
| for (i = 0; i < getInputPortCount(); i++) { |
| TFxPort *port = getInputPort(i); |
| if (port->isConnected()) { |
| TRasterFxP ifx = port->getFx(); |
| assert(ifx); |
| alias += ifx->getAlias(frame, info); |
| } |
| alias += ","; |
| } |
| |
| |
| for (i = 0; i < getParams()->getParamCount(); i++) { |
| TParam *param = getParams()->getParam(i); |
| alias += param->getName() + "=" + param->getValueAlias(frame, 3); |
| } |
| |
| alias += "]"; |
| return alias; |
| } |
| |
| |
| |
| void TRasterFx::dryCompute(TRectD &rect, double frame, |
| const TRenderSettings &info) { |
| if (checkActiveTimeRegion() && !getActiveTimeRegion().contains(frame)) return; |
| |
| if (!getAttributes()->isEnabled() || !m_rasFxImp->isEnabled()) { |
| if (getInputPortCount() == 0) return; |
| |
| TFxPort *port = getInputPort(getPreferredInputPort()); |
| |
| if (port->isConnected()) |
| TRasterFxP(port->getFx())->dryCompute(rect, frame, info); |
| |
| return; |
| } |
| |
| |
| |
| TPoint intTilePos(tfloor(rect.x0), tfloor(rect.y0)); |
| TPointD fracTilePos(rect.x0 - intTilePos.x, rect.y0 - intTilePos.y); |
| TPointD fracInfoTranslation(info.m_affine.a13 - fracTilePos.x, |
| info.m_affine.a23 - fracTilePos.y); |
| TPoint intInfoTranslation(tfloor(fracInfoTranslation.x), |
| tfloor(fracInfoTranslation.y)); |
| TPointD newTilePos(intTilePos.x - intInfoTranslation.x, |
| intTilePos.y - intInfoTranslation.y); |
| |
| if (rect.getP00() != newTilePos) { |
| TRenderSettings newInfo(info); |
| newInfo.m_affine.a13 = fracInfoTranslation.x - intInfoTranslation.x; |
| newInfo.m_affine.a23 = fracInfoTranslation.y - intInfoTranslation.y; |
| |
| TRectD newRect(newTilePos, rect.getSize()); |
| |
| dryCompute(newRect, frame, newInfo); |
| return; |
| } |
| |
| |
| |
| |
| |
| |
| bool canHandleAffine = |
| canHandle(info, frame) || (handledAffine(info, frame) == info.m_affine); |
| if (!info.m_affine.isIdentity() && !canHandleAffine) { |
| TrFx *transformerFx = new TrFx; |
| TFxP locker(transformerFx); |
| transformerFx->setFx(this); |
| transformerFx->dryCompute(rect, frame, info); |
| return; |
| } |
| |
| std::string alias = getAlias(frame, info) + "[" + ::traduce(info.m_affine) + |
| "][" + std::to_string(info.m_bpp) + "]"; |
| |
| int renderStatus = |
| TRenderer::instance().getRenderStatus(TRenderer::renderId()); |
| TFxCacheManager *cacheManager = TFxCacheManager::instance(); |
| |
| if (renderStatus == TRenderer::FIRSTRUN) { |
| TRectD bbox; |
| |
| |
| |
| getBBox(frame, bbox, info); |
| enlargeToI(bbox); |
| |
| TRectD interestingRect(rect * bbox); |
| if (myIsEmpty(interestingRect)) return; |
| |
| |
| ResourceBuilder::declareResource(alias, this, interestingRect, frame, info); |
| |
| doDryCompute(interestingRect, frame, info); |
| } else { |
| TRectD bbox; |
| getBBox(frame, bbox, info); |
| enlargeToI(bbox); |
| |
| TRectD interestingRect(rect * bbox); |
| if (myIsEmpty(interestingRect)) return; |
| |
| |
| FxResourceBuilder rBuilder(alias, this, info, frame); |
| rBuilder.simBuild(interestingRect); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void TRasterFx::doDryCompute(TRectD &rect, double frame, |
| const TRenderSettings &info) { |
| int inputPortCount = getInputPortCount(); |
| for (int i = 0; i < inputPortCount; ++i) { |
| TFxPort *port = getInputPort(i); |
| if (port->isConnected()) { |
| TRectD rectOnInput; |
| TRenderSettings infoOnInput; |
| |
| TRasterFxP fx = port->getFx(); |
| transform(frame, i, rect, info, rectOnInput, infoOnInput); |
| |
| if (!myIsEmpty(rectOnInput)) |
| fx->dryCompute(rectOnInput, frame, infoOnInput); |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| |
| void TRasterFx::allocateAndCompute(TTile &tile, const TPointD &pos, |
| const TDimension &size, TRasterP templateRas, |
| double frame, const TRenderSettings &info) { |
| if (templateRas) { |
| TRaster32P ras32(templateRas); |
| TRaster64P ras64(templateRas); |
| templateRas = 0; |
| |
| TRasterP tileRas; |
| if (ras32) |
| tileRas = TRaster32P(size.lx, size.ly); |
| else if (ras64) |
| tileRas = TRaster64P(size.lx, size.ly); |
| else { |
| assert(false); |
| return; |
| } |
| |
| tile.setRaster(tileRas); |
| } else { |
| if (info.m_bpp == 32) { |
| TRaster32P tileRas(size.lx, size.ly); |
| tile.setRaster(tileRas); |
| } else if (info.m_bpp == 64) { |
| TRaster64P tileRas(size.lx, size.ly); |
| tile.setRaster(tileRas); |
| } else |
| assert(false); |
| } |
| |
| tile.m_pos = pos; |
| compute(tile, frame, info); |
| } |
| |
| |
| |
| |
| |
| void TRasterFx::compute(TTile &tile, double frame, |
| const TRenderSettings &info) { |
| |
| |
| |
| |
| if (checkActiveTimeRegion() && !getActiveTimeRegion().contains(frame)) return; |
| |
| if (!getAttributes()->isEnabled() || !m_rasFxImp->isEnabled()) { |
| if (getInputPortCount() == 0) return; |
| |
| TFxPort *port = getInputPort(0); |
| |
| |
| assert(port->isaControlPort() == false); |
| |
| if (port->isConnected()) { |
| TRasterFxP(port->getFx())->compute(tile, frame, info); |
| return; |
| } |
| |
| return; |
| } |
| |
| |
| |
| |
| TPoint intTilePos(tfloor(tile.m_pos.x), tfloor(tile.m_pos.y)); |
| TPointD fracTilePos(tile.m_pos.x - intTilePos.x, tile.m_pos.y - intTilePos.y); |
| TPointD fracInfoTranslation(info.m_affine.a13 - fracTilePos.x, |
| info.m_affine.a23 - fracTilePos.y); |
| TPoint intInfoTranslation(tfloor(fracInfoTranslation.x), |
| tfloor(fracInfoTranslation.y)); |
| TPointD newTilePos(intTilePos.x - intInfoTranslation.x, |
| intTilePos.y - intInfoTranslation.y); |
| |
| if (tile.m_pos != newTilePos) { |
| |
| TRenderSettings newInfo(info); |
| newInfo.m_affine.a13 = fracInfoTranslation.x - intInfoTranslation.x; |
| newInfo.m_affine.a23 = fracInfoTranslation.y - intInfoTranslation.y; |
| |
| TPointD oldPos(tile.m_pos); |
| tile.m_pos = newTilePos; |
| |
| compute(tile, frame, newInfo); |
| |
| tile.m_pos = oldPos; |
| return; |
| } |
| |
| bool canHandleAffine = |
| canHandle(info, frame) || (handledAffine(info, frame) == info.m_affine); |
| if (!info.m_affine.isIdentity() && !canHandleAffine) { |
| TrFx *transformerFx = new TrFx; |
| TFxP locker(transformerFx); |
| transformerFx->setFx(this); |
| transformerFx->compute(tile, frame, info); |
| return; |
| } |
| |
| |
| TRectD tilePlacement = myConvert(tile.getRaster()->getBounds()) + tile.m_pos; |
| |
| |
| std::string alias = getAlias(frame, info) + "[" + ::traduce(info.m_affine) + |
| "][" + std::to_string(info.m_bpp) + |
| "]"; |
| |
| TRectD bbox; |
| getBBox(frame, bbox, info); |
| enlargeToI(bbox); |
| |
| TRectD interestingRect(tilePlacement * bbox); |
| if (myIsEmpty(interestingRect)) return; |
| |
| |
| TTile interestingTile; |
| interestingTile.m_pos = interestingRect.getP00(); |
| TRect interestingRectI(myConvert(interestingRect - tilePlacement.getP00())); |
| interestingTile.setRaster(tile.getRaster()->extract(interestingRectI)); |
| |
| #ifdef DIAGNOSTICS |
| |
| |
| QString fxName = QString::fromStdString(getDeclaration()->getId()); |
| |
| DIAGNOSTICS_PUSH("FName", fxName); |
| DIAGNOSTICS_THRSET("FComputeTime", 0); |
| |
| TStopWatch sw; |
| sw.start(); |
| |
| #endif |
| |
| |
| FxResourceBuilder rBuilder(alias, this, info, frame); |
| rBuilder.build(interestingTile); |
| |
| #ifdef DIAGNOSTICS |
| sw.stop(); |
| |
| QString countsStr("#ftimes.txt | 4. " + fxName + " | 4. Calls count"); |
| QString fxStr("fcum_4. " + fxName); |
| |
| long computeTime = DIAGNOSTICS_THRGET("FComputeTime"); |
| long fxTime = sw.getTotalTime(); |
| long fxCumulativeTime = DIAGNOSTICS_GLOGET(fxStr); |
| long count = DIAGNOSTICS_GET(countsStr); |
| |
| |
| DIAGNOSTICS_GLOADD(fxStr, computeTime); |
| DIAGNOSTICS_ADD(countsStr, 1); |
| DIAGNOSTICS_SET("#ftimes.txt | 4. " + fxName + " | 3. Mean time", |
| (fxCumulativeTime + computeTime) / (count + 1)); |
| |
| DIAGNOSTICS_POP("FName", 1); |
| QString parentFxName = DIAGNOSTICS_STACKGET("FName"); |
| if (!parentFxName.isEmpty()) |
| DIAGNOSTICS_GLOADD("fcum_4. " + parentFxName, -fxTime); |
| |
| DIAGNOSTICS_GLOADD("fcum_03. Cached Fxs Retrieval", fxTime - computeTime); |
| #endif |
| |
| #ifdef WRITEIMAGES |
| static int iCount = 0; |
| QString qPath("C:\\butta\\image_" + |
| QString::number(++iCount).rightJustified(3, '0') + ".tif"); |
| TImageWriter::save(TFilePath(qPath.toStdWString()), tile.getRaster()); |
| |
| if (iCount >= 35) int aa = 1; |
| #endif |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| } |
| |
| |
| |
| TRasterP TRasterFx::applyAffine(TTile &tileOut, const TTile &tileIn, |
| const TRenderSettings &info) { |
| TAffine aff = info.m_affine; |
| |
| TRasterP src_ras = tileIn.getRaster(); |
| TRasterP dst_ras = tileOut.getRaster(); |
| |
| if (aff.isTranslation()) { |
| |
| TPointD diff(tileOut.m_pos - tileIn.m_pos - TPointD(aff.a13, aff.a23)); |
| double fracX = diff.x - tfloor(diff.x); |
| double fracY = diff.y - tfloor(diff.y); |
| |
| if ((fracX < 0.01 || fracX > 0.99) && (fracY < 0.01 || fracY > 0.99)) { |
| |
| TRect geomIn(src_ras->getBounds()); |
| TRect geomOut(dst_ras->getBounds()); |
| TPoint diffI(convert(diff)); |
| geomIn -= diffI; |
| geomOut += diffI; |
| geomIn *= dst_ras->getBounds(); |
| geomOut *= src_ras->getBounds(); |
| |
| if (geomIn.isEmpty()) return dst_ras; |
| |
| TRasterP rasIn(src_ras->extract(geomOut)); |
| TRasterP rasOut(dst_ras->extract(geomIn)); |
| TRop::copy(rasOut, rasIn); |
| |
| return dst_ras; |
| } |
| } |
| |
| TRectD rectIn = myConvert(src_ras->getBounds()) + tileIn.m_pos; |
| TRectD rectOut = myConvert(dst_ras->getBounds()) + tileOut.m_pos; |
| |
| TRectD rectInAfter = aff * myConvert(src_ras->getBounds()); |
| TAffine rasterAff = TTranslation((aff * rectIn).getP00() - rectOut.getP00() - |
| rectInAfter.getP00()) * |
| aff; |
| |
| TRop::ResampleFilterType qual; |
| switch (info.m_quality) { |
| case TRenderSettings::StandardResampleQuality: |
| qual = TRop::Triangle; |
| break; |
| case TRenderSettings::ImprovedResampleQuality: |
| qual = TRop::Hann2; |
| break; |
| case TRenderSettings::HighResampleQuality: |
| qual = TRop::Hamming3; |
| break; |
| case TRenderSettings::Triangle_FilterResampleQuality: |
| qual = TRop::Triangle; |
| break; |
| case TRenderSettings::Mitchell_FilterResampleQuality: |
| qual = TRop::Mitchell; |
| break; |
| case TRenderSettings::Cubic5_FilterResampleQuality: |
| qual = TRop::Cubic5; |
| break; |
| case TRenderSettings::Cubic75_FilterResampleQuality: |
| qual = TRop::Cubic75; |
| break; |
| case TRenderSettings::Cubic1_FilterResampleQuality: |
| qual = TRop::Cubic1; |
| break; |
| case TRenderSettings::Hann2_FilterResampleQuality: |
| qual = TRop::Hann2; |
| break; |
| case TRenderSettings::Hann3_FilterResampleQuality: |
| qual = TRop::Hann3; |
| break; |
| case TRenderSettings::Hamming2_FilterResampleQuality: |
| qual = TRop::Hamming2; |
| break; |
| case TRenderSettings::Hamming3_FilterResampleQuality: |
| qual = TRop::Hamming3; |
| break; |
| case TRenderSettings::Lanczos2_FilterResampleQuality: |
| qual = TRop::Lanczos2; |
| break; |
| case TRenderSettings::Lanczos3_FilterResampleQuality: |
| qual = TRop::Lanczos3; |
| break; |
| case TRenderSettings::Gauss_FilterResampleQuality: |
| qual = TRop::Gauss; |
| break; |
| case TRenderSettings::ClosestPixel_FilterResampleQuality: |
| qual = TRop::ClosestPixel; |
| break; |
| case TRenderSettings::Bilinear_FilterResampleQuality: |
| qual = TRop::Bilinear; |
| break; |
| default: |
| assert(false); |
| } |
| |
| TRop::resample(dst_ras, src_ras, rasterAff, qual); |
| |
| return dst_ras; |
| } |
| |
| |
| |
| bool TRasterFx::isCacheEnabled() const { return m_rasFxImp->m_cacheEnabled; } |
| |
| |
| |
| void TRasterFx::enableCache(bool on) { m_rasFxImp->enableCache(on); } |
| |
| |
| |
| |
| |
| |
| |
| TRenderSettings::TRenderSettings() |
| : m_gamma(1) |
| , m_timeStretchFrom(25) |
| , m_timeStretchTo(25) |
| , m_stereoscopicShift(0.05) |
| , m_bpp(32) |
| , m_maxTileSize((std::numeric_limits<int>::max)()) |
| , m_shrinkX(1) |
| , m_shrinkY(1) |
| , m_quality(StandardResampleQuality) |
| , m_fieldPrevalence(NoField) |
| , m_stereoscopic(false) |
| , m_isSwatch(false) |
| , m_applyShrinkToViewer(false) |
| , m_userCachable(true) |
| , m_isCanceled(NULL) {} |
| |
| |
| |
| TRenderSettings::~TRenderSettings() {} |
| |
| |
| |
| std::string TRenderSettings::toString() const { |
| std::string ss = |
| std::to_string(m_bpp) + ";" + std::to_string(m_quality) + ";" + |
| std::to_string(m_gamma) + ";" + std::to_string(m_timeStretchFrom) + ";" + |
| std::to_string(m_timeStretchTo) + ";" + |
| std::to_string(m_fieldPrevalence) + ";" + std::to_string(m_shrinkX) + |
| "," + std::to_string(m_shrinkY) + ";" + std::to_string(m_affine.a11) + |
| "," + std::to_string(m_affine.a12) + "," + std::to_string(m_affine.a13) + |
| "," + std::to_string(m_affine.a21) + "," + std::to_string(m_affine.a22) + |
| "," + std::to_string(m_affine.a23) + ";" + std::to_string(m_maxTileSize) + |
| ";" + std::to_string(m_isSwatch) + ";" + std::to_string(m_userCachable) + |
| ";{"; |
| if (!m_data.empty()) { |
| ss += m_data[0]->toString(); |
| for (int i = 1; i < (int)m_data.size(); i++) |
| ss += "," + m_data[i]->toString(); |
| } |
| ss += "}"; |
| return ss; |
| } |
| |
| |
| |
| bool TRenderSettings::operator==(const TRenderSettings &rhs) const { |
| if (m_bpp != rhs.m_bpp || m_quality != rhs.m_quality || |
| m_fieldPrevalence != rhs.m_fieldPrevalence || |
| m_stereoscopic != rhs.m_stereoscopic || |
| m_stereoscopicShift != rhs.m_stereoscopicShift || |
| m_gamma != rhs.m_gamma || m_timeStretchFrom != rhs.m_timeStretchFrom || |
| m_timeStretchTo != rhs.m_timeStretchTo || m_shrinkX != rhs.m_shrinkX || |
| m_shrinkY != rhs.m_shrinkY || |
| m_applyShrinkToViewer != rhs.m_applyShrinkToViewer || |
| m_maxTileSize != rhs.m_maxTileSize || m_affine != rhs.m_affine || |
| m_mark != rhs.m_mark || m_isSwatch != rhs.m_isSwatch || |
| m_userCachable != rhs.m_userCachable) |
| return false; |
| |
| return std::equal(m_data.begin(), m_data.end(), rhs.m_data.begin(), areEqual); |
| } |
| |
| |
| |
| bool TRenderSettings::operator!=(const TRenderSettings &rhs) const { |
| return !operator==(rhs); |
| } |
| |