// TnzCore includes #include "tsystem.h" #include "tstream.h" #include "tthreadmessage.h" #include "tconvert.h" #include "tstopwatch.h" #include "tlevel_io.h" #include "trasterimage.h" #include "ttoonzimage.h" #include "tvectorimage.h" #include "timagecache.h" #include "timageinfo.h" #include "tropcm.h" #include "tofflinegl.h" #include "tvectorrenderdata.h" // TnzBase includes #include "ttzpimagefx.h" #include "trasterfx.h" #include "tzeraryfx.h" #include "trenderer.h" #include "tfxcachemanager.h" // TnzLib includes #include "toonz/toonzscene.h" #include "toonz/txsheet.h" #include "toonz/txshlevelcolumn.h" #include "toonz/txshpalettecolumn.h" #include "toonz/txshzeraryfxcolumn.h" #include "toonz/txshlevel.h" #include "toonz/txshcell.h" #include "toonz/txshsimplelevel.h" #include "toonz/txshpalettelevel.h" #include "toonz/txshleveltypes.h" #include "toonz/levelset.h" #include "toonz/txshchildlevel.h" #include "toonz/fxdag.h" #include "toonz/tcolumnfxset.h" #include "toonz/stage.h" #include "toonz/fill.h" #include "toonz/tstageobjectid.h" #include "toonz/tstageobject.h" #include "toonz/levelproperties.h" #include "toonz/imagemanager.h" #include "toonz/toonzimageutils.h" #include "toonz/tvectorimageutils.h" #include "toonz/preferences.h" #include "toonz/dpiscale.h" #include "imagebuilders.h" // 4.6 compatibility - sandor fxs #include "toonz4.6/raster.h" #include "sandor_fxs/blend.h" extern "C" { #include "sandor_fxs/calligraph.h" #include "sandor_fxs/patternmap.h" } #include "toonz/tcolumnfx.h" //**************************************************************************************** // Preliminaries //**************************************************************************************** #ifdef _WIN32 template class DV_EXPORT_API TFxDeclarationT; template class DV_EXPORT_API TFxDeclarationT; template class DV_EXPORT_API TFxDeclarationT; template class DV_EXPORT_API TFxDeclarationT; #endif TFxDeclarationT columnFxInfo(TFxInfo("Toonz_columnFx", true)); TFxDeclarationT paletteColumnFxInfo( TFxInfo("Toonz_paletteColumnFx", true)); TFxDeclarationT zeraryColumnFxInfo( TFxInfo("Toonz_zeraryColumnFx", true)); TFxDeclarationT infoTXsheetFx(TFxInfo("Toonz_xsheetFx", true)); TFxDeclarationT infoTOutputFx(TFxInfo("Toonz_outputFx", true)); //**************************************************************************************** // Local namespace - misc functions //**************************************************************************************** namespace { void setMaxMatte(TRasterP r) { TRaster32P r32 = (TRaster32P)r; TRaster64P r64 = (TRaster64P)r; if (r32) for (int i = 0; i < r32->getLy(); i++) { TPixel *pix = r32->pixels(i); for (int j = 0; j < r32->getLx(); j++, pix++) pix->m = 255; } else if (r64) for (int i = 0; i < r64->getLy(); i++) { TPixel64 *pix = r64->pixels(i); for (int j = 0; j < r64->getLx(); j++, pix++) pix->m = 65535; } } //--------------------------------------------------------------------------------------------------------- char *strsave(const char *t) { char *s; s = (char *)malloc( strlen(t) + 1); // I'm almost sure that this malloc is LEAKED! Please, check that ! strcpy(s, t); return s; } //------------------------------------------------------------------- inline TRect myConvert(const TRectD &r) { return TRect(tfloor(r.x0), tfloor(r.y0), tceil(r.x1), tceil(r.y1)); } //------------------------------------------------------------------- inline TRect myConvert(const TRectD &r, TPointD &dp) { TRect ri(tfloor(r.x0), tfloor(r.y0), tceil(r.x1), tceil(r.y1)); dp.x = r.x0 - ri.x0; dp.y = r.y0 - ri.y0; assert(dp.x >= 0 && dp.y >= 0); return ri; } //-------------------------------------------------- // Currently used on debug only inline QString traduce(const TRectD &rect) { return "[" + QString::number(rect.x0) + " " + QString::number(rect.y0) + " " + QString::number(rect.x1) + " " + QString::number(rect.y1) + "]"; } //-------------------------------------------------- // Currently used on debug only inline QString traduce(const TRectD &rect, const TRenderSettings &info) { return "[" + QString::number(rect.x0) + " " + QString::number(rect.y0) + " " + QString::number(rect.x1) + " " + QString::number(rect.y1) + "]; aff = (" + QString::number(info.m_affine.a11, 'f') + " " + QString::number(info.m_affine.a12, 'f') + " " + QString::number(info.m_affine.a13, 'f') + " " + QString::number(info.m_affine.a21, 'f') + " " + QString::number(info.m_affine.a22, 'f') + " " + QString::number(info.m_affine.a23, 'f') + ")"; } //------------------------------------------------------------------- inline int colorDistance(const TPixel32 &c0, const TPixel32 &c1) { return (c0.r - c1.r) * (c0.r - c1.r) + (c0.g - c1.g) * (c0.g - c1.g) + (c0.b - c1.b) * (c0.b - c1.b); } //------------------------------------------------------------------- std::string getAlias(TXsheet *xsh, double frame, const TRenderSettings &info) { TFxSet *fxs = xsh->getFxDag()->getTerminalFxs(); std::string alias; // Add the alias for each for (int i = 0; i < fxs->getFxCount(); ++i) { TRasterFx *fx = dynamic_cast(fxs->getFx(i)); assert(fx); if (!fx) continue; alias += fx->getAlias(frame, info) + ";"; } return alias; } } // namespace //**************************************************************************************** // Local namespace - Colormap (Sandor) Fxs stuff //**************************************************************************************** static bool vectorMustApplyCmappedFx( const std::vector &fxs) { std::vector::const_iterator ft, fEnd(fxs.end()); for (ft = fxs.begin(); ft != fEnd; ++ft) { PaletteFilterFxRenderData *paletteFilterData = dynamic_cast(ft->getPointer()); SandorFxRenderData *sandorData = dynamic_cast(ft->getPointer()); // (Daniele) Sandor fxs perform on raster colormaps *only* - while texture // fxs use palette filters to work. // In the latter case, vector-to-colormap conversion makes sure that regions // are not rendered under full ink // pixels (which causes problems**). if (sandorData || (paletteFilterData && paletteFilterData->m_type != ::eApplyToInksAndPaints)) return true; /* (Daniele) Disregarding the above reasons - palette filter fxs do not forcibly return true. Err... ok, my fault - when I wrote that, I forgot to specify WHICH problems** occurred. Whops! Now, it happens that if palette filters DO NOT convert to colormapped forcedly, special styles can be retained... so, let's see what happens. Will have to inquire further, though... */ } return false; } //------------------------------------------------------------------- static bool mustApplySandorFx(const std::vector &fxs) { std::vector::const_iterator ft, fEnd(fxs.end()); for (ft = fxs.begin(); ft != fEnd; ++ft) { SandorFxRenderData *sandorData = dynamic_cast(ft->getPointer()); if (sandorData) return true; } return false; } //------------------------------------------------------------------- static int getEnlargement(const std::vector &fxs, double scale) { int enlargement = 1; std::vector::const_iterator ft, fEnd(fxs.end()); for (ft = fxs.begin(); ft != fEnd; ++ft) { SandorFxRenderData *sandorData = dynamic_cast(ft->getPointer()); if (sandorData) { switch (sandorData->m_type) { case BlendTz: { // Nothing happen, unless we have color 0 among the blended ones. In // such case, // we have to enlarge the bbox proportionally to the amount param. std::vector items; std::string indexes = std::string(sandorData->m_argv[0]); parseIndexes(indexes, items); PaletteFilterFxRenderData paletteFilterData; insertIndexes(items, &paletteFilterData); if (!paletteFilterData.m_colors.empty() && *paletteFilterData.m_colors.begin() == 0) { BlendTzParams ¶ms = sandorData->m_blendParams; enlargement = params.m_amount * scale; } break; } case Calligraphic: case OutBorder: { CalligraphicParams ¶ms = sandorData->m_callParams; enlargement = params.m_thickness * scale; break; } case ArtAtContour: { ArtAtContourParams ¶ms = sandorData->m_contourParams; enlargement = std::max(tceil(sandorData->m_controllerBBox.getLx()), tceil(sandorData->m_controllerBBox.getLy())) * params.m_maxSize; break; } } } } return enlargement; } //------------------------------------------------------------------- static void applyPaletteFilter(TPalette *&plt, bool keep, const std::set &colors, const TPalette *srcPlt) { if (colors.empty()) return; if (!plt) plt = srcPlt->clone(); if (keep) { for (int i = 0; i < plt->getStyleCount(); ++i) { if (colors.find(i) == colors.end()) plt->setStyle(i, TPixel32::Transparent); } } else { std::set::const_iterator ct, cEnd(colors.end()); for (ct = colors.begin(); ct != cEnd; ++ct) { TColorStyle *style = plt->getStyle(*ct); if (style) plt->setStyle(*ct, TPixel32::Transparent); } } } //------------------------------------------------------------------- static TPalette *getPliPalette(const TFilePath &path) { TLevelReaderP levelReader = TLevelReaderP(path); if (!levelReader.getPointer()) return 0; TLevelP level = levelReader->loadInfo(); TPalette *plt = level->getPalette(); return plt ? plt->clone() : (TPalette *)0; } //------------------------------------------------------------------- inline bool fxLess(TRasterFxRenderDataP a, TRasterFxRenderDataP b) { SandorFxRenderData *sandorDataA = dynamic_cast(a.getPointer()); if (!sandorDataA) return false; SandorFxRenderData *sandorDataB = dynamic_cast(b.getPointer()); if (!sandorDataB) return true; int aIndex = sandorDataA->m_type == OutBorder ? 2 : sandorDataA->m_type == BlendTz ? 1 : 0; int bIndex = sandorDataB->m_type == OutBorder ? 2 : sandorDataB->m_type == BlendTz ? 1 : 0; return aIndex < bIndex; } //------------------------------------------------------------------- inline void sortCmappedFxs(std::vector &fxs) { std::stable_sort(fxs.begin(), fxs.end(), fxLess); } //------------------------------------------------------------------- static std::vector getAllBut(std::vector &colorIds) { assert(TPixelCM32::getMaxInk() == TPixelCM32::getMaxPaint()); std::vector curColorIds; std::sort(colorIds.begin(), colorIds.end()); // Taking all colors EXCEPT those in colorIds unsigned int count1 = 0, count2 = 0; int size = TPixelCM32::getMaxInk(); curColorIds.resize(size + 1 - colorIds.size()); for (int i = 0; i < size; i++) if (count1 < colorIds.size() && colorIds[count1] == i) count1++; else curColorIds[count2++] = i; return curColorIds; } //------------------------------------------------------------------- //! \b IMPORTANT \b NOTE: This function is now written so that the passed Toonz //! Image //! will be destroyed at the most appropriate time. You should definitely *COPY* //! all //! necessary information before calling it - however, since the intent was //! that of //! optimizing memory usage, please avoid copying the entire image buffer... static TImageP applyCmappedFx(TToonzImageP &ti, const std::vector &fxs, int frame, double scale) { TImageP result = ti; TTile resultTile; // Just a quick wrapper to the ImageCache TPalette *inPalette, *tempPlt; TPaletteP filteredPalette; TRasterCM32P copyRas; std::string cmRasCacheId; // Retrieve the image dpi double dpiX, dpiY; ti->getDpi(dpiX, dpiY); double dpi = (dpiX > 0) ? dpiX / Stage::inch : 1.0; // First, sort the fxs. std::vector fxsCopy = fxs; sortCmappedFxs(fxsCopy); // First, let's deal with all fxs working on palettes inPalette = ti->getPalette(); std::vector::reverse_iterator it; for (it = fxsCopy.rbegin(); it != fxsCopy.rend(); ++it) { ExternalPaletteFxRenderData *extpltData = dynamic_cast((*it).getPointer()); PaletteFilterFxRenderData *PaletteFilterData = dynamic_cast((*it).getPointer()); if (extpltData && extpltData->m_palette) { filteredPalette = extpltData->m_palette->clone(); filteredPalette->setFrame(frame); } else if (PaletteFilterData && PaletteFilterData->m_type == eApplyToInksAndPaints) { bool keep = PaletteFilterData->m_keep; std::set colors; colors.insert(PaletteFilterData->m_colors.begin(), PaletteFilterData->m_colors.end()); // Apply the palette filters tempPlt = filteredPalette.getPointer(); applyPaletteFilter(tempPlt, keep, colors, inPalette); filteredPalette = tempPlt; inPalette = filteredPalette.getPointer(); } } if (filteredPalette) { // result= ti = ti->clone(); //Is copying truly necessary?? result = ti = TToonzImageP(ti->getRaster(), ti->getSavebox()); filteredPalette->setFrame(frame); ti->setPalette(filteredPalette.getPointer()); } // Next, deal with fxs working on colormaps themselves bool firstSandorFx = true; TRasterCM32P cmRas = ti->getRaster(); TRect tiSaveBox(ti->getSavebox()); TPaletteP tiPalette(ti->getPalette()); ti = 0; // Release the reference to colormap // Now, apply cmapped->cmapped fxs for (it = fxsCopy.rbegin(); it != fxsCopy.rend(); ++it) { PaletteFilterFxRenderData *PaletteFilterData = dynamic_cast(it->getPointer()); if (PaletteFilterData && PaletteFilterData->m_type != eApplyToInksAndPaints) { std::vector indexes; indexes.resize(PaletteFilterData->m_colors.size()); std::set::const_iterator jt = PaletteFilterData->m_colors.begin(); for (int j = 0; j < (int)indexes.size(); ++j, ++jt) indexes[j] = *jt; if (copyRas == TRasterCM32P()) copyRas = cmRas->clone(); // Pixels are literally cleared on a copy buffer /*-- 処理するIndexを反転 --*/ if (PaletteFilterData->m_keep) indexes = getAllBut(indexes); /*-- Paintの消去("Lines Including All * Areas"のみ、Areaに何も操作をしない) --*/ if (PaletteFilterData->m_type != eApplyToInksKeepingAllPaints) // se non e' // eApplyToInksKeepingAllPaints, // sicuramente devo cancellare dei // paint TRop::eraseColors( copyRas, PaletteFilterData->m_type == eApplyToInksDeletingAllPaints ? 0 : &indexes, false); /*-- Inkの消去 --*/ if (PaletteFilterData->m_type != eApplyToPaintsKeepingAllInks) // se non e' // eApplyToPaintsKeepingAllInks, // sicuramente devo cancellare degli // ink TRop::eraseColors( copyRas, PaletteFilterData->m_type == eApplyToPaintsDeletingAllInks ? 0 : &indexes, true, PaletteFilterData->m_type == eApplyToInksAndPaints_NoGap); } } if (copyRas) { cmRas = copyRas; result = TToonzImageP(cmRas, tiSaveBox); result->setPalette(tiPalette.getPointer()); } // Finally, apply cmapped->fullcolor fxs // Prefetch all Blend fxs std::vector blendParams; for (it = fxsCopy.rbegin(); it != fxsCopy.rend(); ++it) { SandorFxRenderData *sandorData = dynamic_cast(it->getPointer()); if (sandorData && sandorData->m_type == BlendTz) { BlendParam param; param.intensity = std::stod(std::string(sandorData->m_argv[3])) * scale * dpi; param.smoothness = sandorData->m_blendParams.m_smoothness; param.stopAtCountour = sandorData->m_blendParams.m_noBlending; param.superSampling = sandorData->m_blendParams.m_superSampling; // Build the color indexes std::vector items; std::string indexes = std::string(sandorData->m_argv[0]); parseIndexes(indexes, items); PaletteFilterFxRenderData paletteFilterData; insertIndexes(items, &paletteFilterData); param.colorsIndexes.reserve(paletteFilterData.m_colors.size()); std::set::iterator it; for (it = paletteFilterData.m_colors.begin(); it != paletteFilterData.m_colors.end(); ++it) param.colorsIndexes.push_back(*it); blendParams.push_back(param); } } // Apply each sandor for (it = fxsCopy.rbegin(); it != fxsCopy.rend(); ++it) { SandorFxRenderData *sandorData = dynamic_cast(it->getPointer()); if (sandorData) { // Avoid dealing with specific cases if ((sandorData->m_type == BlendTz && blendParams.empty()) || (sandorData->m_type == OutBorder && !firstSandorFx)) continue; if (!firstSandorFx) { // Retrieve the colormap from cache cmRas = TToonzImageP(TImageCache::instance()->get(cmRasCacheId, true)) ->getRaster(); // Apply a palette filter in order to keep only the colors specified in // the sandor argv std::vector items; std::string indexes = std::string(sandorData->m_argv[0]); parseIndexes(indexes, items); PaletteFilterFxRenderData paletteFilterData; insertIndexes(items, &paletteFilterData); filteredPalette = tempPlt = 0; applyPaletteFilter(tempPlt, true, paletteFilterData.m_colors, tiPalette.getPointer()); filteredPalette = tempPlt; } else { // Pass the input colormap to the cache and release its reference as // final result cmRasCacheId = TImageCache::instance()->getUniqueId(); TImageCache::instance()->add(cmRasCacheId, TToonzImageP(cmRas, tiSaveBox)); result = 0; } // Convert current smart pointers to a 4.6 'fashion'. The former ones are // released - so they // do not occupy smart object references. RASTER *oldRasterIn, *oldRasterOut; oldRasterIn = TRop::convertRaster50to46( cmRas, filteredPalette ? filteredPalette : tiPalette.getPointer()); cmRas = TRasterCM32P(0); { TRaster32P rasterOut(TDimension(oldRasterIn->lx, oldRasterIn->ly)); oldRasterOut = TRop::convertRaster50to46(rasterOut, 0); } switch (sandorData->m_type) { case BlendTz: { if (blendParams.empty()) continue; // Retrieve the colormap from cache cmRas = TToonzImageP(TImageCache::instance()->get(cmRasCacheId, true)) ->getRaster(); TToonzImageP ti(cmRas, tiSaveBox); ti->setPalette(filteredPalette ? filteredPalette.getPointer() : tiPalette.getPointer()); TRasterImageP riOut(TImageCache::instance()->get( std::string(oldRasterOut->cacheId, oldRasterOut->cacheIdLength), true)); TRaster32P rasterOut = riOut->getRaster(); blend(ti, rasterOut, blendParams); blendParams.clear(); break; } case Calligraphic: case OutBorder: { if (sandorData->m_type == OutBorder && !firstSandorFx) continue; const char *argv[12]; memcpy(argv, sandorData->m_argv, 12 * sizeof(const char *)); double thickness = std::stod(std::string(sandorData->m_argv[7])) * scale * dpi; argv[7] = strsave(std::to_string(thickness).c_str()); calligraph(oldRasterIn, oldRasterOut, sandorData->m_border, sandorData->m_argc, argv, sandorData->m_shrink, sandorData->m_type == OutBorder); break; } case ArtAtContour: { const char *argv[12]; memcpy(argv, sandorData->m_argv, 12 * sizeof(const char *)); double distance = std::stod(std::string(sandorData->m_argv[6])) * scale * dpi; argv[6] = strsave(std::to_string(distance).c_str()); distance = std::stod(std::string(sandorData->m_argv[7])) * scale * dpi; argv[7] = strsave(std::to_string(distance).c_str()); double density = std::stod(std::string(sandorData->m_argv[8])) / sq(scale * dpi); argv[8] = strsave(std::to_string(density).c_str()); double size = std::stod(std::string(sandorData->m_argv[1])) * scale * dpi; argv[1] = strsave(std::to_string(size).c_str()); size = std::stod(std::string(sandorData->m_argv[2])) * scale * dpi; argv[2] = strsave(std::to_string(size).c_str()); RASTER *imgContour = TRop::convertRaster50to46(sandorData->m_controller, 0); patternmap(oldRasterIn, oldRasterOut, sandorData->m_border, sandorData->m_argc, argv, sandorData->m_shrink, imgContour); TRop::releaseRaster46(imgContour); break; } default: assert(false); } TRasterImageP riOut(TImageCache::instance()->get( std::string(oldRasterOut->cacheId, oldRasterOut->cacheIdLength), true)); TRaster32P rasterOut = riOut->getRaster(); TRop::releaseRaster46(oldRasterIn); TRop::releaseRaster46(oldRasterOut); if (firstSandorFx) { resultTile.setRaster(rasterOut); firstSandorFx = false; } else TRop::over(resultTile.getRaster(), rasterOut); } } // Release cmRas cache identifier if any TImageCache::instance()->remove(cmRasCacheId); if (!result) result = TRasterImageP(resultTile.getRaster()); return result; } //------------------------------------------------------------------- static void applyCmappedFx(TVectorImageP &vi, const std::vector &fxs, int frame) { TRasterP ras; bool keep = false; TPaletteP modPalette; std::set colors; std::vector::const_iterator it = fxs.begin(); // prima tutti gli effetti che agiscono sulla paletta.... for (; it != fxs.end(); ++it) { ExternalPaletteFxRenderData *extpltData = dynamic_cast((*it).getPointer()); PaletteFilterFxRenderData *pltFilterData = dynamic_cast((*it).getPointer()); if (extpltData && extpltData->m_palette) modPalette = extpltData->m_palette->clone(); else if (pltFilterData) { assert( pltFilterData->m_type == eApplyToInksAndPaints); // Must have been converted to CM32 otherwise keep = pltFilterData->m_keep; colors.insert(pltFilterData->m_colors.begin(), pltFilterData->m_colors.end()); } } TPalette *tempPlt = modPalette.getPointer(); applyPaletteFilter(tempPlt, keep, colors, vi->getPalette()); modPalette = tempPlt; if (modPalette) { vi = vi->clone(); vi->setPalette(modPalette.getPointer()); } } //**************************************************************************************** // LevelFxResourceBuilder definition //**************************************************************************************** class LevelFxBuilder final : public ResourceBuilder { TRasterP m_loadedRas; TPaletteP m_palette; TXshSimpleLevel *m_sl; TFrameId m_fid; TRectD m_tileGeom; bool m_64bit; TRect m_rasBounds; public: LevelFxBuilder(const std::string &resourceName, double frame, const TRenderSettings &rs, TXshSimpleLevel *sl, TFrameId fid) : ResourceBuilder(resourceName, 0, frame, rs) , m_loadedRas() , m_palette() , m_sl(sl) , m_fid(fid) , m_64bit(rs.m_bpp == 64) {} void setRasBounds(const TRect &rasBounds) { m_rasBounds = rasBounds; } void compute(const TRectD &tileRect) override { // Load the image TImageP img(m_sl->getFullsampledFrame( m_fid, (m_64bit ? ImageManager::is64bitEnabled : 0) | ImageManager::dontPutInCache)); if (!img) return; TRasterImageP rimg(img); TToonzImageP timg(img); m_loadedRas = rimg ? (TRasterP)rimg->getRaster() : timg ? (TRasterP)timg->getRaster() : TRasterP(); assert(m_loadedRas); if (timg) m_palette = timg->getPalette(); assert(tileRect == TRectD(0, 0, m_loadedRas->getLx(), m_loadedRas->getLy())); } void simCompute(const TRectD &rect) override {} void upload(TCacheResourceP &resource) override { assert(m_loadedRas); resource->upload(TPoint(), m_loadedRas); if (m_palette) resource->uploadPalette(m_palette); } bool download(TCacheResourceP &resource) override { // If the image has been loaded in this builder, just use it if (m_loadedRas) return true; // If the image has yet to be loaded by this builder, skip without // allocating anything if (resource->canDownloadAll(m_rasBounds)) { m_loadedRas = resource->buildCompatibleRaster(m_rasBounds.getSize()); resource->downloadPalette(m_palette); return resource->downloadAll(TPoint(), m_loadedRas); } else return false; } TImageP getImage() const { if (!m_loadedRas) return TImageP(); TRasterCM32P cm(m_loadedRas); TImageP result(cm ? TImageP(TToonzImageP(cm, cm->getBounds())) : TImageP(TRasterImageP(m_loadedRas))); if (m_palette) result->setPalette(m_palette.getPointer()); return result; } }; //**************************************************************************************** // TLevelColumnFx implementation //**************************************************************************************** TLevelColumnFx::TLevelColumnFx() : m_levelColumn(0), m_isCachable(true), m_mutex(), m_offlineContext(0) { setName(L"LevelColumn"); } //-------------------------------------------------- TLevelColumnFx::~TLevelColumnFx() { if (m_offlineContext) delete m_offlineContext; } //------------------------------------------------------------------- bool TLevelColumnFx::canHandle(const TRenderSettings &info, double frame) { // NOTE 1: Currently, it is necessary that level columns return FALSE for // raster levels - just a quick way to tell the cache functions that they // have to be cached. if (!m_levelColumn) return true; int row = m_levelColumn->getFirstRow(); TXshCell cell = m_levelColumn->getCell(row); if (cell.isEmpty() && Preferences::instance()->isImplicitHoldEnabled()) { int r0, r1; m_levelColumn->getRange(r0, r1); for (int r = std::min(r1, row); r >= r0; r--) { cell = m_levelColumn->getCell(r); if (cell.isEmpty()) continue; break; } } if (cell.isEmpty()) return true; TXshSimpleLevel *sl = cell.m_level->getSimpleLevel(); if (!sl) return true; return (sl->getType() == PLI_XSHLEVEL && !vectorMustApplyCmappedFx(info.m_data)); } //------------------------------------------------------------------- TAffine TLevelColumnFx::handledAffine(const TRenderSettings &info, double frame) { if (!m_levelColumn) return TAffine(); int row = m_levelColumn->getFirstRow(); TXshCell cell = m_levelColumn->getCell(row); if (cell.isEmpty() && Preferences::instance()->isImplicitHoldEnabled()) { int r0, r1; m_levelColumn->getRange(r0, r1); for (int r = std::min(r1, row); r >= r0; r--) { cell = m_levelColumn->getCell(r); if (cell.isEmpty()) continue; break; } } if (cell.isEmpty()) return TAffine(); TXshSimpleLevel *sl = cell.m_level->getSimpleLevel(); if (!sl) return TAffine(); if (sl->getType() == PLI_XSHLEVEL) return vectorMustApplyCmappedFx(info.m_data) ? TRasterFx::handledAffine(info, frame) : info.m_affine; // Accept any translation consistent with the image's pixels geometry TImageInfo imageInfo; getImageInfo(imageInfo, sl, cell.m_frameId); TPointD pixelsOrigin(-0.5 * imageInfo.m_lx, -0.5 * imageInfo.m_ly); const TAffine &aff = info.m_affine; if (aff.a11 != 1.0 || aff.a22 != 1.0 || aff.a12 != 0.0 || aff.a21 != 0.0) return TTranslation(-pixelsOrigin); // This is a translation, ok. Just ensure it is consistent. TAffine consistentAff(aff); consistentAff.a13 -= pixelsOrigin.x, consistentAff.a23 -= pixelsOrigin.y; consistentAff.a13 = tfloor(consistentAff.a13), consistentAff.a23 = tfloor(consistentAff.a23); consistentAff.a13 += pixelsOrigin.x, consistentAff.a23 += pixelsOrigin.y; return consistentAff; } //------------------------------------------------------------------- TFilePath TLevelColumnFx::getPalettePath(int frame) const { if (!m_levelColumn) return TFilePath(); TXshCell cell = m_levelColumn->getCell(frame); if (cell.isEmpty() && Preferences::instance()->isImplicitHoldEnabled()) { int r0, r1; m_levelColumn->getRange(r0, r1); for (int r = std::min(r1, frame); r >= r0; r--) { cell = m_levelColumn->getCell(r); if (cell.isEmpty()) continue; break; } } if (cell.isEmpty()) return TFilePath(); TXshSimpleLevel *sl = cell.m_level->getSimpleLevel(); if (!sl) return TFilePath(); if (sl->getType() == TZP_XSHLEVEL) return sl->getScene()->decodeFilePath( sl->getPath().withNoFrame().withType("tpl")); if (sl->getType() == PLI_XSHLEVEL) return sl->getScene()->decodeFilePath(sl->getPath()); return TFilePath(); } //------------------------------------------------------------------- TPalette *TLevelColumnFx::getPalette(int frame) const { if (!m_levelColumn) return 0; TXshCell cell = m_levelColumn->getCell(frame); if (cell.isEmpty() && Preferences::instance()->isImplicitHoldEnabled()) { int r0, r1; m_levelColumn->getRange(r0, r1); for (int r = std::min(r1, frame); r >= r0; r--) { cell = m_levelColumn->getCell(r); if (cell.isEmpty()) continue; break; } } if (cell.isEmpty()) return 0; TXshSimpleLevel *sl = cell.m_level->getSimpleLevel(); if (!sl) return 0; return sl->getPalette(); } //-------------------------------------------------- TFx *TLevelColumnFx::clone(bool recursive) const { TLevelColumnFx *clonedFx = dynamic_cast(TFx::clone(recursive)); assert(clonedFx); clonedFx->m_levelColumn = m_levelColumn; clonedFx->m_isCachable = m_isCachable; return clonedFx; } //-------------------------------------------------- void TLevelColumnFx::doDryCompute(TRectD &rect, double frame, const TRenderSettings &info) { if (!m_levelColumn) return; int row = (int)frame; TXshCell cell = m_levelColumn->getCell(row); if (cell.isEmpty() && Preferences::instance()->isImplicitHoldEnabled()) { int r0, r1; m_levelColumn->getRange(r0, r1); for (int r = std::min(r1, row); r >= r0; r--) { cell = m_levelColumn->getCell(r); if (cell.isEmpty()) continue; break; } } if (cell.isEmpty()) return; TXshSimpleLevel *sl = cell.m_level->getSimpleLevel(); if (!sl) return; // In case this is a vector level, the image is renderized quickly and // directly at the // correct resolution. Caching is disabled in such case, at the moment. if (sl->getType() == PLI_XSHLEVEL) return; int renderStatus = TRenderer::instance().getRenderStatus(TRenderer::renderId()); std::string alias = getAlias(frame, TRenderSettings()) + "_image"; TImageInfo imageInfo; getImageInfo(imageInfo, sl, cell.m_frameId); TRectD imgRect(0, 0, imageInfo.m_lx, imageInfo.m_ly); if (renderStatus == TRenderer::FIRSTRUN) { ResourceBuilder::declareResource(alias, 0, imgRect, frame, info, false); } else { LevelFxBuilder builder(alias, frame, info, sl, cell.m_frameId); builder.setRasBounds(TRect(0, 0, imageInfo.m_lx - 1, imageInfo.m_ly - 1)); builder.simBuild(imgRect); } } //-------------------------------------------------- bool isSubsheetChainOnColumn0(TXsheet *topXsheet, TXsheet *subsheet, int frame) { if (topXsheet == subsheet) return true; const TXshCell cell = topXsheet->getCell(frame, 0); if (!cell.m_level) return false; TXshChildLevel *cl = cell.m_level->getChildLevel(); if (!cl) return false; return isSubsheetChainOnColumn0(cl->getXsheet(), subsheet, frame); } //----------------------------------------------------- void TLevelColumnFx::doCompute(TTile &tile, double frame, const TRenderSettings &info) { if (!m_levelColumn) return; // Ensure that a corresponding cell and level exists int row = (int)frame; TXshCell cell = m_levelColumn->getCell(row); if (cell.isEmpty() && Preferences::instance()->isImplicitHoldEnabled()) { int r0, r1; m_levelColumn->getRange(r0, r1); for (int r = std::min(r1, row); r >= r0; r--) { cell = m_levelColumn->getCell(r); if (cell.isEmpty()) continue; break; } } if (cell.isEmpty()) return; TXshSimpleLevel *sl = cell.m_level->getSimpleLevel(); if (!sl) return; TFrameId fid = cell.m_frameId; TImageP img; TImageInfo imageInfo; // Now, fetch the image if (sl->getType() != PLI_XSHLEVEL) { // Raster case LevelFxBuilder builder(getAlias(frame, TRenderSettings()) + "_image", frame, info, sl, fid); getImageInfo(imageInfo, sl, fid); TRectD imgRect(0, 0, imageInfo.m_lx, imageInfo.m_ly); builder.setRasBounds(TRect(0, 0, imageInfo.m_lx - 1, imageInfo.m_ly - 1)); builder.build(imgRect); img = builder.getImage(); } else { // Vector case (loading is immediate) if (!img) { img = sl->getFullsampledFrame( fid, ((info.m_bpp == 64) ? ImageManager::is64bitEnabled : 0) | ImageManager::dontPutInCache); } } // Extract the required geometry TRect tileBounds(tile.getRaster()->getBounds()); TRectD tileRectD = TRectD(tileBounds.x0, tileBounds.y0, tileBounds.x1 + 1, tileBounds.y1 + 1) + tile.m_pos; // To be sure, if there is no image, return. if (!img) return; TRectD bBox = img->getBBox(); // Discriminate image type if (TVectorImageP vectorImage = img) { // Vector case if (vectorMustApplyCmappedFx(info.m_data)) { // Deal separately applyTzpFxsOnVector(vectorImage, tile, frame, info); } else { QMutexLocker m(&m_mutex); bBox = info.m_affine * vectorImage->getBBox(); TDimension size(tile.getRaster()->getSize()); TAffine aff = TTranslation(-tile.m_pos.x, -tile.m_pos.y) * TScale(1.0 / info.m_shrinkX, 1.0 / info.m_shrinkY) * info.m_affine; applyCmappedFx(vectorImage, info.m_data, (int)frame); TPalette *vpalette = vectorImage->getPalette(); assert(vpalette); m_isCachable = !vpalette->isAnimated(); int oldFrame = vpalette->getFrame(); TVectorRenderData rd(TVectorRenderData::ProductionSettings(), aff, TRect(size), vpalette); if (!m_offlineContext || m_offlineContext->getLx() < size.lx || m_offlineContext->getLy() < size.ly) { if (m_offlineContext) delete m_offlineContext; m_offlineContext = new TOfflineGL(size); } m_offlineContext->makeCurrent(); m_offlineContext->clear(TPixel32(0, 0, 0, 0)); // If level has animated palette, it is necessary to lock palette's color // against // concurrents TPalette::setFrame. if (!m_isCachable) vpalette->mutex()->lock(); vpalette->setFrame((int)frame); m_offlineContext->draw(vectorImage, rd, true); vpalette->setFrame(oldFrame); if (!m_isCachable) vpalette->mutex()->unlock(); m_offlineContext->getRaster(tile.getRaster()); m_offlineContext->doneCurrent(); } } else { // Raster case TRasterP ras; TAffine aff; TRasterImageP ri = img; TToonzImageP ti = img; img = 0; if (ri) { // Fullcolor case ras = ri->getRaster(); ri = 0; TRaster32P ras32(ras); TRaster64P ras64(ras); // Ensure that ras is either a 32 or 64 fullcolor. // Otherwise, we have to convert it. if (!ras32 && !ras64) { TRasterP tileRas(tile.getRaster()); TRaster32P tileRas32(tileRas); TRaster64P tileRas64(tileRas); if (tileRas32) { ras32 = TRaster32P(ras->getLx(), ras->getLy()); TRop::convert(ras32, ras); ras = ras32; } else if (tileRas64) { ras64 = TRaster64P(ras->getLx(), ras->getLy()); TRop::convert(ras64, ras); ras = ras64; } else assert(0); } TXsheet *xsh = m_levelColumn->getLevelColumn()->getXsheet(); TXsheet *txsh = sl->getScene()->getTopXsheet(); LevelProperties *levelProp = sl->getProperties(); if (Preferences::instance()->isIgnoreAlphaonColumn1Enabled() && m_levelColumn->getIndex() == 0 && isSubsheetChainOnColumn0(txsh, xsh, frame)) { TRasterP appRas(ras->clone()); setMaxMatte(appRas); ras = appRas; } if (levelProp->doPremultiply()) { TRasterP appRas = ras->clone(); TRop::premultiply(appRas); ras = appRas; } if (levelProp->whiteTransp()) { TRasterP appRas(ras->clone()); TRop::whiteTransp(appRas); ras = appRas; } if (levelProp->antialiasSoftness() > 0) { TRasterP appRas = ras->create(ras->getLx(), ras->getLy()); TRop::antialias(ras, appRas, 10, levelProp->antialiasSoftness()); ras = appRas; } if (TXshSimpleLevel::m_fillFullColorRaster) { TRasterP appRas(ras->clone()); FullColorAreaFiller filler(appRas); TPaletteP palette = new TPalette(); int styleId = palette->getPage(0)->addStyle(TPixel32::White); FillParameters params; params.m_palette = palette.getPointer(); params.m_styleId = styleId; params.m_minFillDepth = 0; params.m_maxFillDepth = 15; filler.rectFill(appRas->getBounds(), params, false); ras = appRas; } } else if (ti) { // Colormap case // Use the imageInfo's dpi ti->setDpi(imageInfo.m_dpix, imageInfo.m_dpiy); TImageP newImg = applyTzpFxs(ti, frame, info); ri = newImg; ti = newImg; if (ri) ras = ri->getRaster(); else ras = ti->getRaster(); if (sl->getProperties()->antialiasSoftness() > 0) { // convert colormap raster to fullcolor raster before applying antialias if (ti) { TRaster32P convRas(ras->getSize()); TRop::convert(convRas, ras, ti->getPalette(), TRect(), false, true); ras = convRas; } TRasterP appRas = ras->create(ras->getLx(), ras->getLy()); TRop::antialias(ras, appRas, 10, sl->getProperties()->antialiasSoftness()); ras = appRas; } ri = 0; } if (ras) { double lx_2 = ras->getLx() / 2.0; double ly_2 = ras->getLy() / 2.0; TRenderSettings infoAux(info); assert(info.m_affine.isTranslation()); infoAux.m_data.clear(); // Place the output rect in the image's reference tileRectD += TPointD(lx_2 - info.m_affine.a13, ly_2 - info.m_affine.a23); // Then, retrieve loaded image's interesting region TRectD inTileRectD; if (ti) { TRect saveBox(ti->getSavebox()); inTileRectD = TRectD(saveBox.x0, saveBox.y0, saveBox.x1 + 1, saveBox.y1 + 1); } else { TRect rasBounds(ras->getBounds()); inTileRectD = TRectD(rasBounds.x0, rasBounds.y0, rasBounds.x1 + 1, rasBounds.y1 + 1); } // And intersect the two inTileRectD *= tileRectD; // Should the intersection be void, return if (inTileRectD.x0 >= inTileRectD.x1 || inTileRectD.y0 >= inTileRectD.y1) return; // Output that intersection in the requested tile TRect inTileRect(tround(inTileRectD.x0), tround(inTileRectD.y0), tround(inTileRectD.x1) - 1, tround(inTileRectD.y1) - 1); TTile inTile(ras->extract(inTileRect), inTileRectD.getP00() + TPointD(-lx_2, -ly_2)); // Observe that inTile is in the standard reference, ie image's minus the // center coordinates if ((TRasterCM32P)ras) { // In the colormapped case, we have to convert the cmap to fullcolor TPalette *palette = ti->getPalette(); if (!m_isCachable) palette->mutex()->lock(); int prevFrame = palette->getFrame(); palette->setFrame((int)frame); TTile auxtile(TRaster32P(inTile.getRaster()->getSize()), inTile.m_pos); TRop::convert(auxtile, inTile, palette, false, true); palette->setFrame(prevFrame); if (!m_isCachable) palette->mutex()->unlock(); inTile.setRaster(auxtile.getRaster()); } TRasterFx::applyAffine(tile, inTile, infoAux); } } } //------------------------------------------------------------------- TImageP TLevelColumnFx::applyTzpFxs(TToonzImageP &ti, double frame, const TRenderSettings &info) { double scale = sqrt(fabs(info.m_affine.det())); int prevFrame = ti->getPalette()->getFrame(); m_isCachable = !ti->getPalette()->isAnimated(); if (!m_isCachable) ti->getPalette()->mutex()->lock(); TPaletteP palette(ti->getPalette()); palette->setFrame((int)frame); TImageP newImg = applyCmappedFx(ti, info.m_data, (int)frame, scale); palette->setFrame(prevFrame); if (!m_isCachable) palette->mutex()->unlock(); return newImg; } //------------------------------------------------------------------- void TLevelColumnFx::applyTzpFxsOnVector(const TVectorImageP &vi, TTile &tile, double frame, const TRenderSettings &info) { TRect tileBounds(tile.getRaster()->getBounds()); TRectD tileRectD = TRectD(tileBounds.x0, tileBounds.y0, tileBounds.x1 + 1, tileBounds.y1 + 1) + tile.m_pos; // Info's affine is not the identity only in case the loaded image is a vector // one. double scale = sqrt(fabs(info.m_affine.det())); int enlargement = getEnlargement(info.m_data, scale); // Extract the vector image's groups std::vector groupsList; getGroupsList(vi, groupsList); // For each group, apply the tzp fxs stored in info. The result is immediately // converted // to a raster image if necessary. unsigned int i; for (i = 0; i < groupsList.size(); ++i) { TVectorImageP &groupVi = groupsList[i]; // Extract the group's bbox. TRectD groupBBox(info.m_affine * groupVi->getBBox()); if (!mustApplySandorFx(info.m_data)) groupBBox *= tileRectD; groupBBox = groupBBox.enlarge(enlargement); if (groupBBox.x0 >= groupBBox.x1 || groupBBox.y0 >= groupBBox.y1) continue; // Ensure that groupBBox and tile have the same integer geometry groupBBox -= tile.m_pos; groupBBox.x0 = tfloor(groupBBox.x0); groupBBox.y0 = tfloor(groupBBox.y0); groupBBox.x1 = tceil(groupBBox.x1); groupBBox.y1 = tceil(groupBBox.y1); groupBBox += tile.m_pos; // Build groupBBox's relative position to the tile TPoint groupRelativePosToTile(groupBBox.x0 - tile.m_pos.x, groupBBox.y0 - tile.m_pos.y); // Convert the group to a strictly sufficient Toonz image TToonzImageP groupTi = ToonzImageUtils::vectorToToonzImage( groupVi, info.m_affine, groupVi->getPalette(), groupBBox.getP00(), TDimension(groupBBox.getLx(), groupBBox.getLy()), &info.m_data, true); // Apply the tzp fxs to the converted Toonz image TImageP groupResult = applyTzpFxs(groupTi, frame, info); // If necessary, convert the result to fullcolor TRasterImageP groupRi = groupResult; if (!groupRi) { groupTi = groupResult; assert(groupTi); TRasterP groupTiRas(groupTi->getRaster()); TRaster32P tempRas(groupTiRas->getSize()); groupRi = TRasterImageP(tempRas); TRop::convert(tempRas, groupTiRas, groupTi->getPalette()); } // Over the group image on the output TRasterP tileRas(tile.getRaster()); TRop::over(tileRas, groupRi->getRaster(), groupRelativePosToTile); } } //------------------------------------------------------------------- int TLevelColumnFx::getMemoryRequirement(const TRectD &rect, double frame, const TRenderSettings &info) { // Sandor fxs are currently considered *VERY* inefficient upon tile // subdivision if (mustApplySandorFx(info.m_data)) { return -1; } return 0; } //------------------------------------------------------------------- void TLevelColumnFx::getImageInfo(TImageInfo &info, TXshSimpleLevel *sl, TFrameId frameId) { int type = sl->getType(); assert(type != PLI_XSHLEVEL); if (type == PLI_XSHLEVEL) return; std::string imageId = sl->getImageId(frameId); const TImageInfo *storedInfo = ImageManager::instance()->getInfo(imageId, ImageManager::none, 0); if (!storedInfo) // sulle pict caricate info era nullo, ma l'immagine c'e'! // con la getFullSampleFrame riprendo l'immagine e ricalcolo la savebox... { TImageP img; if (!(img = sl->getFullsampledFrame(frameId, ImageManager::dontPutInCache))) { assert(false); return; } // Raster levels from ffmpeg were not giving the right dimensions without // the raster cast and check TRasterImageP rasterImage = (TRasterImageP)img; if (rasterImage) { info.m_lx = (int)rasterImage->getRaster()->getLx(); info.m_ly = (int)rasterImage->getRaster()->getLy(); } else { info.m_lx = (int)img->getBBox().getLx(); info.m_ly = (int)img->getBBox().getLy(); } info.m_x0 = info.m_y0 = 0; info.m_x1 = (int)img->getBBox().getP11().x; info.m_y1 = (int)img->getBBox().getP11().y; } else info = *storedInfo; } //------------------------------------------------------------------- bool TLevelColumnFx::doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) { // Usual preliminaries (make sure a level/cell exists, etc...) if (!m_levelColumn) return false; int row = (int)frame; TXshCell cell = m_levelColumn->getCell(row); if (cell.isEmpty() && Preferences::instance()->isImplicitHoldEnabled()) { int r0, r1; m_levelColumn->getRange(r0, r1); for (int r = std::min(r1, row); r >= r0; r--) { cell = m_levelColumn->getCell(r); if (cell.isEmpty()) continue; break; } } if (cell.isEmpty()) return false; TXshLevelP xshl = cell.m_level; if (!xshl) return false; TXshSimpleLevel *sl = xshl->getSimpleLevel(); if (!sl) return false; double dpi = 1.0; // Discriminate for level type int type = xshl->getType(); if (type != PLI_XSHLEVEL) { TImageInfo imageInfo; getImageInfo(imageInfo, sl, cell.m_frameId); double cx = 0.5 * imageInfo.m_lx; double cy = 0.5 * imageInfo.m_ly; if (info.m_getFullSizeBBox) { bBox = TRectD(-cx, -cy, cx, cy); } else { TRect imageSavebox(imageInfo.m_x0, imageInfo.m_y0, imageInfo.m_x1, imageInfo.m_y1); double x0 = (imageSavebox.x0 - cx); double y0 = (imageSavebox.y0 - cy); double x1 = x0 + imageSavebox.getLx(); double y1 = y0 + imageSavebox.getLy(); bBox = TRectD(x0, y0, x1, y1); } dpi = imageInfo.m_dpix / Stage::inch; } else { TXshCell cell = m_levelColumn->getCell(row); if (cell.isEmpty() && Preferences::instance()->isImplicitHoldEnabled()) { int r0, r1; m_levelColumn->getRange(r0, r1); for (int r = std::min(r1, row); r >= r0; r--) { cell = m_levelColumn->getCell(r); if (cell.isEmpty()) continue; break; } } TImageP img = cell.getImage(false); if (!img) return false; bBox = img->getBBox(); } // Add the enlargement of the bbox due to Tzp render data if (info.m_data.size()) { TRectD imageBBox(bBox); for (unsigned int i = 0; i < info.m_data.size(); ++i) { TRectD enlargedImageBBox = info.m_data[i]->getBBoxEnlargement(imageBBox); double enlargement = enlargedImageBBox.x1 - imageBBox.x1; bBox += imageBBox.enlarge(enlargement * dpi); } } return true; } //------------------------------------------------------------------- const TPersistDeclaration *TLevelColumnFx::getDeclaration() const { return &columnFxInfo; } //------------------------------------------------------------------- std::string TLevelColumnFx::getPluginId() const { return "Toonz_"; } //------------------------------------------------------------------- TFxTimeRegion TLevelColumnFx::getTimeRegion() const { if (!m_levelColumn) return TFxTimeRegion(); int first = m_levelColumn->getFirstRow(); int last = m_levelColumn->getRowCount(); return TFxTimeRegion(first, last); } //------------------------------------------------------------------- void TLevelColumnFx::setColumn(TXshLevelColumn *column) { m_levelColumn = column; } //------------------------------------------------------------------- std::wstring TLevelColumnFx::getColumnId() const { if (!m_levelColumn) return L"Col?"; return L"Col" + std::to_wstring(m_levelColumn->getIndex() + 1); } //------------------------------------------------------------------- std::wstring TLevelColumnFx::getColumnName() const { if (!m_levelColumn) return L""; int idx = getColumnIndex(); return ::to_wstring(m_levelColumn->getXsheet() ->getStageObject(TStageObjectId::ColumnId(idx)) ->getName()); } //------------------------------------------------------------------- std::string TLevelColumnFx::getAlias(double frame, const TRenderSettings &info) const { if (!m_levelColumn) return std::string(); TXshCell cell = m_levelColumn->getCell((int)frame); if (cell.isEmpty() && Preferences::instance()->isImplicitHoldEnabled()) { int r0, r1; m_levelColumn->getRange(r0, r1); for (int r = std::min(r1, (int)frame); r >= r0; r--) { cell = m_levelColumn->getCell(r); if (cell.isEmpty()) continue; break; } } if (cell.isEmpty()) return std::string(); TFilePath fp; TXshSimpleLevel *sl = cell.getSimpleLevel(); if (!sl) { // Try with the sub-xsheet case TXshChildLevel *childLevel = cell.m_level->getChildLevel(); if (childLevel) return ::getAlias(childLevel->getXsheet(), frame, info); return std::string(); } TFilePath path = sl->getPath(); if (!cell.m_frameId.isNoFrame()) fp = path.withFrame(cell.m_frameId); else fp = path; std::string rdata; std::vector::const_iterator it = info.m_data.begin(); for (; it != info.m_data.end(); ++it) { TRasterFxRenderDataP data = *it; if (data) rdata += data->toString(); } if (sl->getType() == PLI_XSHLEVEL || sl->getType() == TZP_XSHLEVEL) { TPalette *palette = cell.getPalette(); if (palette && palette->isAnimated()) rdata += "animatedPlt" + std::to_string(frame); } if (Preferences::instance()->isIgnoreAlphaonColumn1Enabled()) { TXsheet *xsh = m_levelColumn->getLevelColumn()->getXsheet(); TXsheet *txsh = sl->getScene()->getTopXsheet(); if (m_levelColumn->getIndex() == 0 && isSubsheetChainOnColumn0(txsh, xsh, frame)) rdata += "column_0"; } return getFxType() + "[" + ::to_string(fp.getWideString()) + "," + rdata + "]"; } //------------------------------------------------------------------- int TLevelColumnFx::getColumnIndex() const { return m_levelColumn ? m_levelColumn->getIndex() : -1; } //------------------------------------------------------------------- TXshColumn *TLevelColumnFx::getXshColumn() const { return m_levelColumn; } //------------------------------------------------------------------- TAffine TLevelColumnFx::getDpiAff(int frame) { if (!m_levelColumn) return TAffine(); TXshCell cell = m_levelColumn->getCell(frame); if (cell.isEmpty() && Preferences::instance()->isImplicitHoldEnabled()) { int r0, r1; m_levelColumn->getRange(r0, r1); for (int r = std::min(r1, frame); r >= r0; r--) { cell = m_levelColumn->getCell(r); if (cell.isEmpty()) continue; break; } } if (cell.isEmpty()) return TAffine(); TXshSimpleLevel *sl = cell.m_level->getSimpleLevel(); TFrameId fid = cell.m_frameId; if (sl) return ::getDpiAffine(sl, cell.m_frameId, true); return TAffine(); } //------------------------------------------------------------------- void TLevelColumnFx::saveData(TOStream &os) { assert(m_levelColumn); TFx::saveData(os); } //------------------------------------------------------------------- void TLevelColumnFx::loadData(TIStream &is) { // The internal numeric identifier is set here but not in constructors. // This is *intended* as fx cloning needs to retain the original id // through the fxs tree deployment procedure happening just before a // render process (see scenefx.cpp). TFx::loadData(is); setNewIdentifier(); } //**************************************************************************************** // TPaletteColumnFx implementation //**************************************************************************************** TPaletteColumnFx::TPaletteColumnFx() : m_paletteColumn(0) {} //------------------------------------------------------------------- TPaletteColumnFx::~TPaletteColumnFx() {} //-------------------------------------------------- TFx *TPaletteColumnFx::clone(bool recursive) const { TPaletteColumnFx *clonedFx = dynamic_cast(TFx::clone(recursive)); assert(clonedFx); clonedFx->m_paletteColumn = m_paletteColumn; return clonedFx; } //------------------------------------------------------------------- void TPaletteColumnFx::doCompute(TTile &tile, double frame, const TRenderSettings &) {} //------------------------------------------------------------------- bool TPaletteColumnFx::doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) { return false; } //------------------------------------------------------------------- TAffine TPaletteColumnFx::getDpiAff(int frame) { return TAffine(); } //------------------------------------------------------------------- bool TPaletteColumnFx::canHandle(const TRenderSettings &info, double frame) { return false; } //------------------------------------------------------------------- TFilePath TPaletteColumnFx::getPalettePath(int frame) const { if (!m_paletteColumn) return TFilePath(); TXshCell cell = m_paletteColumn->getCell(frame); if (cell.isEmpty() || cell.m_level->getPaletteLevel() == 0) return TFilePath(); TXshPaletteLevel *paletteLevel = cell.m_level->getPaletteLevel(); TFilePath path = paletteLevel->getPath(); path = paletteLevel->getScene()->decodeFilePath(path); return path; } //------------------------------------------------------------------- TPalette *TPaletteColumnFx::getPalette(int frame) const { if (!m_paletteColumn) return 0; TXshCell cell = m_paletteColumn->getCell(frame); if (cell.isEmpty() || cell.m_level->getPaletteLevel() == 0) return 0; TXshPaletteLevel *paletteLevel = cell.m_level->getPaletteLevel(); return paletteLevel->getPalette(); } //------------------------------------------------------------------- const TPersistDeclaration *TPaletteColumnFx::getDeclaration() const { return &paletteColumnFxInfo; } //------------------------------------------------------------------- std::string TPaletteColumnFx::getPluginId() const { return "Toonz_"; } //------------------------------------------------------------------- TFxTimeRegion TPaletteColumnFx::getTimeRegion() const { int first = 0; int last = 11; return TFxTimeRegion(first, last); } //------------------------------------------------------------------- std::wstring TPaletteColumnFx::getColumnName() const { if (!m_paletteColumn) return L"Col?"; return L"Col" + std::to_wstring(m_paletteColumn->getIndex() + 1); } //------------------------------------------------------------------- std::wstring TPaletteColumnFx::getColumnId() const { if (!m_paletteColumn) return L"Col?"; return L"Col" + std::to_wstring(m_paletteColumn->getIndex() + 1); } //------------------------------------------------------------------- std::string TPaletteColumnFx::getAlias(double frame, const TRenderSettings &info) const { TFilePath palettePath = getPalettePath(frame); return "TPaletteColumnFx[" + ::to_string(palettePath.getWideString()) + "]"; } //------------------------------------------------------------------- int TPaletteColumnFx::getColumnIndex() const { return m_paletteColumn ? m_paletteColumn->getIndex() : -1; } //------------------------------------------------------------------- TXshColumn *TPaletteColumnFx::getXshColumn() const { return m_paletteColumn; } //**************************************************************************************** // TZeraryColumnFx implementation //**************************************************************************************** TZeraryColumnFx::TZeraryColumnFx() : m_zeraryFxColumn(0), m_fx(0) { setName(L"ZeraryColumn"); } //------------------------------------------------------------------- TZeraryColumnFx::~TZeraryColumnFx() { if (m_zeraryFxColumn) m_zeraryFxColumn->release(); if (m_fx) { m_fx->m_columnFx = 0; m_fx->release(); } } //------------------------------------------------------------------- void TZeraryColumnFx::doCompute(TTile &tile, double frame, const TRenderSettings &info) { if (m_zeraryFxColumn) { TRasterFx *fx = dynamic_cast(m_fx); if (fx) fx->compute(tile, frame, info); } } //------------------------------------------------------------------- bool TZeraryColumnFx::doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) { if (m_zeraryFxColumn) { TRasterFx *fx = dynamic_cast(m_fx); if (fx) return fx->doGetBBox(frame, bBox, info); } return false; } //------------------------------------------------------------------- const TPersistDeclaration *TZeraryColumnFx::getDeclaration() const { return &zeraryColumnFxInfo; } //------------------------------------------------------------------- std::string TZeraryColumnFx::getPluginId() const { return "Toonz_"; } //------------------------------------------------------------------- TFxTimeRegion TZeraryColumnFx::getTimeRegion() const { return TFxTimeRegion(0, (std::numeric_limits::max)()); } //------------------------------------------------------------------- void TZeraryColumnFx::setColumn(TXshZeraryFxColumn *column) { m_zeraryFxColumn = column; } //------------------------------------------------------------------- std::wstring TZeraryColumnFx::getColumnName() const { return getZeraryFx()->getName(); } //------------------------------------------------------------------- std::wstring TZeraryColumnFx::getColumnId() const { return getZeraryFx()->getFxId(); } //------------------------------------------------------------------- void TZeraryColumnFx::setZeraryFx(TFx *fx) { TZeraryFx *zfx = static_cast(fx); if (fx) { assert(dynamic_cast(fx)); fx->addRef(); fx->setNewIdentifier(); // When adopting zerary fxs, ensure that they // have a new numeric identifier zfx->m_columnFx = this; } if (m_fx) { m_fx->m_columnFx = 0; m_fx->release(); } m_fx = zfx; } //------------------------------------------------------------------- std::string TZeraryColumnFx::getAlias(double frame, const TRenderSettings &info) const { return "TZeraryColumnFx[" + m_fx->getAlias(frame, info) + "]"; } //------------------------------------------------------------------- void TZeraryColumnFx::loadData(TIStream &is) { if (m_fx) m_fx->release(); m_fx = 0; TPersist *p = 0; is >> p; m_fx = dynamic_cast(p); if (m_fx) { m_fx->addRef(); m_fx->m_columnFx = this; m_fx->setNewIdentifier(); } TFx::loadData(is); setNewIdentifier(); // Same as with TColumnFx } //------------------------------------------------------------------- void TZeraryColumnFx::saveData(TOStream &os) { assert(m_fx); os << m_fx; TFx::saveData(os); } //------------------------------------------------------------------- int TZeraryColumnFx::getColumnIndex() const { return m_zeraryFxColumn ? m_zeraryFxColumn->getIndex() : -1; } //------------------------------------------------------------------- TXshColumn *TZeraryColumnFx::getXshColumn() const { return m_zeraryFxColumn; } //**************************************************************************************** // TXSheetFx implementation //**************************************************************************************** const TPersistDeclaration *TXsheetFx::getDeclaration() const { return &infoTXsheetFx; } //------------------------------------------------------------------- TXsheetFx::TXsheetFx() : m_fxDag(0) { setName(L"Xsheet"); } //------------------------------------------------------------------- void TXsheetFx::doCompute(TTile &tile, double frame, const TRenderSettings &) {} //------------------------------------------------------------------- bool TXsheetFx::doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) { return false; } //------------------------------------------------------------------- std::string TXsheetFx::getPluginId() const { return "Toonz_"; } //------------------------------------------------------------------- std::string TXsheetFx::getAlias(double frame, const TRenderSettings &info) const { std::string alias = getFxType(); alias += "["; // Add each terminal fx's alias TFxSet *terminalFxs = m_fxDag->getTerminalFxs(); int i, fxsCount = terminalFxs->getFxCount(); for (i = 0; i < fxsCount; ++i) { alias += static_cast(terminalFxs->getFx(i))->getAlias(frame, info) + ","; } return alias + "]"; } //------------------------------------------------------------------- void TXsheetFx::setFxDag(FxDag *fxDag) { m_fxDag = fxDag; } //**************************************************************************************** // TOutputFx implementation //**************************************************************************************** TOutputFx::TOutputFx() { addInputPort("source", m_input); setName(L"Output"); } //------------------------------------------------------------------- const TPersistDeclaration *TOutputFx::getDeclaration() const { return &infoTOutputFx; } //------------------------------------------------------------------- void TOutputFx::doCompute(TTile &tile, double frame, const TRenderSettings &) {} //------------------------------------------------------------------- bool TOutputFx::doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) { return false; } //------------------------------------------------------------------- std::string TOutputFx::getPluginId() const { return "Toonz_"; }