tahoma2d/toonz/sources/toonzlib/tcolumnfx.cpp
2023-02-25 20:59:02 -05:00

1953 lines
62 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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;
TRasterFP rF = (TRasterFP)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;
}
else if (rF)
for (int i = 0; i < rF->getLy(); i++) {
TPixelF *pix = rF->pixels(i);
for (int j = 0; j < rF->getLx(); j++, pix++)
pix->m = TPixelF::maxChannelValue;
}
}
//---------------------------------------------------------------------------------------------------------
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 &params = sandorData->m_blendParams;
enlargement = params.m_amount * scale;
}
break;
}
case Calligraphic:
case OutBorder: {
CalligraphicParams &params = sandorData->m_callParams;
enlargement = params.m_thickness * scale;
break;
}
case ArtAtContour: {
ArtAtContourParams &params = 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;
int m_bpp;
// bool m_linear;
// 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_bpp(rs.m_bpp) {}
//, m_linear(rs.m_linearColorSpace){}
void setRasBounds(const TRect &rasBounds) { m_rasBounds = rasBounds; }
void compute(const TRectD &tileRect) override {
UCHAR flag = ImageManager::dontPutInCache;
if (m_bpp == 64 || m_bpp == 128) flag = flag | ImageManager::is64bitEnabled;
if (m_bpp == 128) flag = flag | ImageManager::isFloatEnabled;
// if (m_linear)
// flag = flag | ImageManager::isLinearEnabled;
// Load the image
TImageP img(m_sl->getFullsampledFrame(m_fid, flag));
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");
enableComputeInFloat(true);
}
//--------------------------------------------------
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()) 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()) 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);
TRasterFP rasF(ras);
// Ensure that ras is either a 32 or 64 fullcolor.
// Otherwise, we have to convert it.
if (!ras32 && !ras64 && !rasF) {
TRasterP tileRas(tile.getRaster());
TRaster32P tileRas32(tileRas);
TRaster64P tileRas64(tileRas);
TRasterFP tileRasF(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 if (tileRasF) {
rasF = TRasterFP(ras->getLx(), ras->getLy());
TRop::convert(rasF, ras);
ras = rasF;
} 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 &&
!rasF) { // temporarily disabled with float raster
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;
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 {
TXshCell cell = m_levelColumn->getCell(row);
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(bool ignoreImplicit) const {
if (!m_levelColumn) return TFxTimeRegion();
int first = m_levelColumn->getFirstRow();
int last = m_levelColumn->getRowCount();
// For implicit hold, if the last frame is not a stop frame, it's held
// indefinitely
if (Preferences::instance()->isImplicitHoldEnabled() && !ignoreImplicit &&
!m_levelColumn->getCell(last - 1).getFrameId().isStopFrame())
return TFxTimeRegion(0, (std::numeric_limits<double>::max)());
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()) 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<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(bool ignoreImplicit) 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(bool ignoreImplicit) 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_"; }