// 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<TLevelColumnFx>;
template class DV_EXPORT_API TFxDeclarationT<TZeraryColumnFx>;
template class DV_EXPORT_API TFxDeclarationT<TXsheetFx>;
template class DV_EXPORT_API TFxDeclarationT<TOutputFx>;
#endif
TFxDeclarationT<TLevelColumnFx> columnFxInfo(TFxInfo("Toonz_columnFx", true));
TFxDeclarationT<TPaletteColumnFx> paletteColumnFxInfo(
TFxInfo("Toonz_paletteColumnFx", true));
TFxDeclarationT<TZeraryColumnFx> zeraryColumnFxInfo(
TFxInfo("Toonz_zeraryColumnFx", true));
TFxDeclarationT<TXsheetFx> infoTXsheetFx(TFxInfo("Toonz_xsheetFx", true));
TFxDeclarationT<TOutputFx> 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<TRasterFx *>(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<TRasterFxRenderDataP> &fxs) {
std::vector<TRasterFxRenderDataP>::const_iterator ft, fEnd(fxs.end());
for (ft = fxs.begin(); ft != fEnd; ++ft) {
PaletteFilterFxRenderData *paletteFilterData =
dynamic_cast<PaletteFilterFxRenderData *>(ft->getPointer());
SandorFxRenderData *sandorData =
dynamic_cast<SandorFxRenderData *>(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<TRasterFxRenderDataP> &fxs) {
std::vector<TRasterFxRenderDataP>::const_iterator ft, fEnd(fxs.end());
for (ft = fxs.begin(); ft != fEnd; ++ft) {
SandorFxRenderData *sandorData =
dynamic_cast<SandorFxRenderData *>(ft->getPointer());
if (sandorData) return true;
}
return false;
}
//-------------------------------------------------------------------
static int getEnlargement(const std::vector<TRasterFxRenderDataP> &fxs,
double scale) {
int enlargement = 1;
std::vector<TRasterFxRenderDataP>::const_iterator ft, fEnd(fxs.end());
for (ft = fxs.begin(); ft != fEnd; ++ft) {
SandorFxRenderData *sandorData =
dynamic_cast<SandorFxRenderData *>(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<std::string> 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<int> &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<int>::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<SandorFxRenderData *>(a.getPointer());
if (!sandorDataA) return false;
SandorFxRenderData *sandorDataB =
dynamic_cast<SandorFxRenderData *>(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<TRasterFxRenderDataP> &fxs) {
std::stable_sort(fxs.begin(), fxs.end(), fxLess);
}
//-------------------------------------------------------------------
static std::vector<int> getAllBut(std::vector<int> &colorIds) {
assert(TPixelCM32::getMaxInk() == TPixelCM32::getMaxPaint());
std::vector<int> 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<TRasterFxRenderDataP> &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<TRasterFxRenderDataP> fxsCopy = fxs;
sortCmappedFxs(fxsCopy);
// First, let's deal with all fxs working on palettes
inPalette = ti->getPalette();
std::vector<TRasterFxRenderDataP>::reverse_iterator it;
for (it = fxsCopy.rbegin(); it != fxsCopy.rend(); ++it) {
ExternalPaletteFxRenderData *extpltData =
dynamic_cast<ExternalPaletteFxRenderData *>((*it).getPointer());
PaletteFilterFxRenderData *PaletteFilterData =
dynamic_cast<PaletteFilterFxRenderData *>((*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<int> 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<PaletteFilterFxRenderData *>(it->getPointer());
if (PaletteFilterData &&
PaletteFilterData->m_type != eApplyToInksAndPaints) {
std::vector<int> indexes;
indexes.resize(PaletteFilterData->m_colors.size());
std::set<int>::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<BlendParam> blendParams;
for (it = fxsCopy.rbegin(); it != fxsCopy.rend(); ++it) {
SandorFxRenderData *sandorData =
dynamic_cast<SandorFxRenderData *>(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<std::string> 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<int>::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<SandorFxRenderData *>(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<std::string> 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<TRasterFxRenderDataP> &fxs,
int frame) {
TRasterP ras;
bool keep = false;
TPaletteP modPalette;
std::set<int> colors;
std::vector<TRasterFxRenderDataP>::const_iterator it = fxs.begin();
// prima tutti gli effetti che agiscono sulla paletta....
for (; it != fxs.end(); ++it) {
ExternalPaletteFxRenderData *extpltData =
dynamic_cast<ExternalPaletteFxRenderData *>((*it).getPointer());
PaletteFilterFxRenderData *pltFilterData =
dynamic_cast<PaletteFilterFxRenderData *>((*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;
TXshCell cell = m_levelColumn->getCell(m_levelColumn->getFirstRow());
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();
TXshCell cell = m_levelColumn->getCell(m_levelColumn->getFirstRow());
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()) 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()) 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<TLevelColumnFx *>(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()) 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()) 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);
// obtain jaggy image when the Closest Pixel is set
if (info.m_quality == TRenderSettings::ClosestPixel_FilterResampleQuality)
rd.m_antiAliasing = false;
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<TVectorImageP> 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;
const TXshCell &cell = m_levelColumn->getCell(row);
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 {
TImageP img = m_levelColumn->getCell(row).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 || m_levelColumn->getCell((int)frame).isEmpty())
return std::string();
const TXshCell &cell = m_levelColumn->getCell((int)frame);
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<TRasterFxRenderDataP>::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()) 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<TPaletteColumnFx *>(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<TRasterFx *>(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<TRasterFx *>(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<double>::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<TZeraryFx *>(fx);
if (fx) {
assert(dynamic_cast<TZeraryFx *>(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<TZeraryFx *>(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<TRasterFx *>(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_"; }