Merge pull request #1142 from manongjohn/clipping_mask_column

Clipping Mask Column
This commit is contained in:
manongjohn 2023-10-20 21:51:41 -04:00 committed by GitHub
commit 32a45890bb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 862 additions and 112 deletions

View file

@ -684,6 +684,80 @@ public:
}
};
//******************************************************************************************
// ColumnMaskFx definition
//
// This is similar to InFx/OutFx but is not visible to the user. It is meant to
// work with and use the original Mask column logic of the normal viewer so
// that the results are the same.
//******************************************************************************************
class ColumnMaskFx final : public TBaseRasterFx {
FX_DECLARATION(ColumnMaskFx)
TRasterFxPort m_source, m_mask;
public:
ColumnMaskFx() {
addInputPort("Source", m_source);
addInputPort("Mask", m_mask);
setName(L"ColumnMaskFx");
enableComputeInFloat(true);
}
~ColumnMaskFx() {}
bool doGetBBox(double frame, TRectD &bbox,
const TRenderSettings &info) override {
if (m_source.isConnected()) {
TRenderSettings maskInfo(info);
maskInfo.m_useMaskBox = true;
return m_source->doGetBBox(frame, bbox, maskInfo);
}
bbox = TRectD();
return false;
}
bool canHandle(const TRenderSettings &info, double frame) override {
return true;
}
void doCompute(TTile &tile, double frame,
const TRenderSettings &ri) override {
if (!m_source.isConnected()) return;
TRenderSettings maskRi(ri);
maskRi.m_useMaskBox = true;
TTile srcTile;
m_source->allocateAndCompute(srcTile, tile.m_pos,
tile.getRaster()->getSize(), tile.getRaster(),
frame, maskRi);
maskRi.m_applyMask = true;
m_mask->compute(srcTile, frame, maskRi);
// Replace original tile with masked tile
TRop::copy(tile.getRaster(), srcTile.getRaster());
}
void doDryCompute(TRectD &rect, double frame,
const TRenderSettings &info) override {
if (!m_source.isConnected()) return;
m_source->dryCompute(rect, frame, info);
if (m_mask.isConnected()) m_mask->dryCompute(rect, frame, info);
}
int getMemoryRequirement(const TRectD &rect, double frame,
const TRenderSettings &info) override {
return TRasterFx::memorySize(rect, info.m_bpp);
}
};
//==================================================================
//=======================
@ -706,3 +780,5 @@ FX_IDENTIFIER(BlendFx, "blendFx")
FX_IDENTIFIER(ColorDodgeFx, "colorDodgeFx")
FX_IDENTIFIER(ColorBurnFx, "colorBurnFx")
FX_IDENTIFIER(ScreenFx, "screenFx")
//--------
FX_IDENTIFIER(ColumnMaskFx, "columnMaskFx")

View file

@ -70,6 +70,9 @@ public:
Imp();
void copy(Imp *src);
void resetMask();
void updateOpenGlState();
void pushMask();
@ -121,6 +124,34 @@ TStencilControl::Imp::Imp()
//---------------------------------------------------------
void TStencilControl::Imp::copy(Imp *src) {
m_stencilBitCount = src->m_stencilBitCount;
m_pushCount = src->m_pushCount;
m_currentWriting = src->m_currentWriting;
m_enabledMask = src->m_enabledMask;
m_writingMask = src->m_writingMask;
m_inOrOutMask = src->m_inOrOutMask;
m_drawOnScreenMask = src->m_drawOnScreenMask;
m_drawOnlyOnceMask = src->m_drawOnlyOnceMask;
m_virtualState = src->m_virtualState;
}
//---------------------------------------------------------
void TStencilControl::Imp::resetMask() {
m_stencilBitCount = 0;
m_pushCount = 1;
m_currentWriting = -1;
m_enabledMask = 0;
m_writingMask = 0;
m_inOrOutMask = 0;
m_drawOnScreenMask = 0;
m_drawOnlyOnceMask = 0;
m_virtualState = 0;
}
//---------------------------------------------------------
TStencilControl *TStencilControl::instance() {
StencilControlManager *instance = StencilControlManager::instance();
return instance->getCurrentStencilControl();
@ -128,7 +159,7 @@ TStencilControl *TStencilControl::instance() {
//---------------------------------------------------------
TStencilControl::TStencilControl() : m_imp(new Imp) {}
TStencilControl::TStencilControl() : m_imp(new Imp) { m_impStack.empty(); }
//---------------------------------------------------------
@ -364,3 +395,46 @@ void TStencilControl::disableMask() {
}
//---------------------------------------------------------
bool TStencilControl::isMaskEnabled() { return m_imp->m_virtualState > 0; }
//---------------------------------------------------------
void TStencilControl::stashMask() {
Imp *currentImp = new Imp;
currentImp->copy(m_imp.get());
m_impStack.push(currentImp);
int maskCount = m_imp->m_pushCount;
if (m_imp->m_virtualState == 2)
for (int i = 0; i < maskCount; i++) endMask();
else if (m_imp->m_virtualState == 1) {
for (int i = 0; i < maskCount; i++) disableMask();
}
}
//---------------------------------------------------------
void TStencilControl::restoreMask() {
int maskCount = m_impStack.top()->m_pushCount;
if (m_impStack.top()->m_virtualState == 2) {
for (int i = 0; i < maskCount; i++) {
if (i > 0) endMask();
beginMask();
}
} else if (m_impStack.top()->m_virtualState == 1) {
for (int i = 1; i <= maskCount; i++) {
beginMask();
endMask();
unsigned char currentMask = 1 << (i - 1);
if ((m_impStack.top()->m_inOrOutMask & currentMask) != 0)
enableMask(SHOW_INSIDE);
else
enableMask(SHOW_OUTSIDE);
}
}
m_imp->copy(m_impStack.top());
m_impStack.pop();
}

View file

@ -685,6 +685,30 @@ void TOfflineGL::draw(TVectorImageP image, const TVectorRenderData &rd,
//-----------------------------------------------------------------------------
void TOfflineGL::drawMask(TVectorImageP image, const TVectorRenderData &rd,
bool doInitMatrix) {
checkErrorsByGL;
makeCurrent();
checkErrorsByGL;
if (doInitMatrix) {
initMatrix();
checkErrorsByGL;
}
if (image) {
checkErrorsByGL;
tglDrawMask(rd, image.getPointer());
checkErrorsByGL;
}
checkErrorsByGL;
glFlush();
checkErrorsByGL;
}
//-----------------------------------------------------------------------------
void TOfflineGL::draw(TRasterImageP ri, const TAffine &aff, bool doInitMatrix) {
makeCurrent();

View file

@ -123,6 +123,14 @@ bool computeOutline(const TRegion *region,
//-------------------------------------------------------------------
void OutlineRegionProp::computeRegionOutline() {
// Avoid overlapping calculations
if (!m_outline.setCalculating()) return;
// Drawing in progress. Hold calculation until it's done
while (m_outline.isInUse()) {
// Wait
}
int subRegionNumber = getRegion()->getSubregionCount();
TRegionOutline::PointVector app;
@ -142,6 +150,7 @@ void OutlineRegionProp::computeRegionOutline() {
}
m_outline.m_bbox = getRegion()->getBBox();
m_outline.unsetCalculating();
}
//-------------------------------------------------------------------

View file

@ -199,6 +199,13 @@ void TglTessellator::doTessellate(GLTess &glTess, const TColorFunction *cf,
void TglTessellator::doTessellate(GLTess &glTess, const TColorFunction *cf,
const bool antiAliasing,
TRegionOutline &outline) {
// Calculation in progress. Hold drawing until it's done
while (outline.isCalculating()) {
// Wait
}
outline.setInUse();
QMutexLocker sl(&CombineDataGuard);
Combine_data.clear();
@ -221,8 +228,8 @@ void TglTessellator::doTessellate(GLTess &glTess, const TColorFunction *cf,
#endif
#endif
for (TRegionOutline::Boundary::iterator poly_it = outline.m_exterior.begin();
poly_it != outline.m_exterior.end(); ++poly_it) {
for (int y = 0; y < outline.m_exterior.size(); y++) {
TRegionOutline::PointVector *poly_it = &outline.m_exterior[y];
#ifdef GLU_VERSION_1_2
gluTessBeginContour(glTess.m_tess);
#else
@ -233,9 +240,10 @@ void TglTessellator::doTessellate(GLTess &glTess, const TColorFunction *cf,
#endif
#endif
for (TRegionOutline::PointVector::iterator it = poly_it->begin();
it != poly_it->end(); ++it)
for (int z = 0; z < poly_it->size(); z++) {
T3DPointD *it = &poly_it->at(z);
gluTessVertex(glTess.m_tess, &(it->x), &(it->x));
}
#ifdef GLU_VERSION_1_2
gluTessEndContour(glTess.m_tess);
@ -282,6 +290,8 @@ void TglTessellator::doTessellate(GLTess &glTess, const TColorFunction *cf,
endIt = Combine_data.end();
beginIt = Combine_data.begin();
for (; beginIt != endIt; ++beginIt) delete[](*beginIt);
outline.unsetInUse();
}
//------------------------------------------------------------------

View file

@ -140,7 +140,8 @@ enum class PredefinedRect {
// ADD_LEVEL,
FOOTER_NOTE_OBJ_AREA,
FOOTER_NOTE_AREA,
NAVIGATION_TAG_AREA
NAVIGATION_TAG_AREA,
CLIPPING_MASK_AREA
};
enum class PredefinedLine {
LOCKED, //! dotted vertical line when cell is locked

View file

@ -39,6 +39,9 @@ DVAPI void deleteKeyframes(const TFxP &fx, int frame);
DVAPI void setKeyframe(const TFxP &dstFx, int dstFrame, const TFxP &srcFx,
int srcFrame, bool changedOnly = false);
DVAPI TFxP makeMask(const TFxP &source, const TFxP &mask);
} // namespace TFxUtil
#endif

View file

@ -70,6 +70,9 @@ public:
void draw(TVectorImageP image, const TVectorRenderData &rd,
bool doInitMatrix = false);
void drawMask(TVectorImageP image, const TVectorRenderData &rd,
bool doInitMatrix = false);
void draw(TRasterImageP image, const TAffine &aff, bool doInitMatrix = false);
void flush();

View file

@ -44,6 +44,20 @@ namespace Stage {
DVVAR extern const double inch;
DVVAR extern const double standardDpi;
//=============================================================================
/*! The ZPlacement class preserve camera position information.
*/
//=============================================================================
class ZPlacement {
public:
TAffine m_aff;
double m_z;
ZPlacement() : m_aff(), m_z(0) {}
ZPlacement(const TAffine &aff, double z) : m_aff(aff), m_z(z) {}
};
class Visitor;
struct VisitArgs;

View file

@ -118,6 +118,10 @@ public:
bool m_currentDrawingOnTop;
bool m_isMask;
bool m_isInvertedMask;
bool m_canRenderMask;
public:
Player();

View file

@ -7,6 +7,7 @@
#include "timage.h"
#include "trastercm.h"
#include "tgl.h"
#include "tstencilcontrol.h"
// TnzExt includes
#include "ext/plasticvisualsettings.h"
@ -106,7 +107,8 @@ public:
// used in Toonz derivative works such as Tab or LineTest. They deal with
// OpenGL stencil buffer.
virtual void enableMask() = 0;
virtual void enableMask(
TStencilControl::MaskType maskType = TStencilControl::SHOW_INSIDE) = 0;
virtual void disableMask() = 0;
virtual void beginMask() = 0;
@ -278,7 +280,8 @@ public:
void beginMask() override;
void endMask() override;
void enableMask() override;
void enableMask(TStencilControl::MaskType maskType =
TStencilControl::SHOW_INSIDE) override;
void disableMask() override;
int getNodesCount();
@ -319,7 +322,8 @@ public:
void onRasterImage(TRasterImage *ri, const Stage::Player &data) override{};
void beginMask() override;
void endMask() override;
void enableMask() override;
void enableMask(TStencilControl::MaskType maskType =
TStencilControl::SHOW_INSIDE) override;
void disableMask() override;
int getColumnIndex() const;
@ -343,6 +347,8 @@ class DVAPI OpenGlPainter final : public Visitor // Yep, the name sucks...
bool m_isViewer, m_alphaEnabled, m_paletteHasChanged;
double m_minZ;
bool m_singleColumnEnabled;
public:
OpenGlPainter(const TAffine &viewAff, const TRect &rect,
const ImagePainter::VisualSettings &vs, bool isViewer,
@ -360,10 +366,14 @@ public:
void beginMask() override;
void endMask() override;
void enableMask() override;
void enableMask(TStencilControl::MaskType maskType =
TStencilControl::SHOW_INSIDE) override;
void disableMask() override;
double getMinZ() const { return m_minZ; }
void enableSingleColumn(bool on) { m_singleColumnEnabled = on; }
bool isSingleColumnEnabled() const { return m_singleColumnEnabled; }
};
} // namespace Stage

View file

@ -78,7 +78,9 @@ protected:
ePreviewVisible = 0x2,
eLocked = 0x8,
eMasked = 0x10,
eCamstandTransparent43 = 0x20 // obsoleto, solo per retrocompatibilita'
eCamstandTransparent43 = 0x20, // obsoleto, solo per retrocompatibilita'
eInvertedMask = 0x80,
eRenderMask = 0x100
};
TRaster32P m_icon;
@ -192,11 +194,16 @@ Return true if column is a mask.
\sa setMask()
*/
bool isMask() const;
bool isInvertedMask() const;
bool canRenderMask() const;
/*!
Set column status mask to \b on.
\sa isMask()
*/
void setIsMask(bool on);
void setInvertedMask(bool on);
void setCanRenderMask(bool on);
virtual bool isEmpty() const { return true; }

View file

@ -88,6 +88,8 @@ Return \b TFx.
// Used in TCellData::getNumbers
bool setNumbers(int row, int rowCount, const TXshCell cells[]);
std::vector<TXshColumn *> getColumnMasks();
private:
// not implemented
TXshLevelColumn(const TXshLevelColumn &);

View file

@ -144,14 +144,16 @@ QPixmap DVAPI convertImageToPixmap(const QImage &image);
QImage DVAPI
generateIconImage(const QString &iconSVGName, qreal opacity = qreal(1.0),
QSize newSize = QSize(),
Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio);
Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio,
bool useThemeColor = true);
//-----------------------------------------------------------------------------
QPixmap DVAPI
generateIconPixmap(const QString &iconSVGName, qreal opacity = qreal(1.0),
QSize newSize = QSize(),
Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio);
Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio,
bool useThemeColor = true);
//-----------------------------------------------------------------------------

View file

@ -167,6 +167,11 @@ public:
double m_colorSpaceGamma;
bool m_applyMask;
bool m_invertedMask;
bool m_useMaskBox;
bool m_plasticMask;
public:
TRenderSettings();
~TRenderSettings();

View file

@ -13,12 +13,31 @@ public:
TRectD m_bbox;
TRegionOutline() : m_doAntialiasing(false) {}
bool m_calculating;
int m_inUse;
TRegionOutline()
: m_doAntialiasing(false), m_calculating(false), m_inUse(0) {}
void clear() {
m_exterior.clear();
m_interior.clear();
}
bool setCalculating() {
if (m_calculating) return false;
m_calculating = true;
return true;
}
void unsetCalculating() { m_calculating = false; }
int isCalculating() { return m_calculating; }
void setInUse() { m_inUse++; }
void unsetInUse() {
m_inUse--;
if (m_inUse < 0) m_inUse = 0;
}
int isInUse() { return m_inUse > 0; }
};
#endif

View file

@ -4,6 +4,7 @@
#define TSTENCILCONTROL_H
#include <memory>
#include <stack>
#include "tcommon.h"
@ -34,6 +35,8 @@ private:
class Imp;
std::unique_ptr<Imp> m_imp;
std::stack<Imp *> m_impStack;
public:
static TStencilControl *instance();
@ -45,6 +48,11 @@ public:
void enableMask(MaskType maskType);
void disableMask();
bool isMaskEnabled();
void stashMask();
void restoreMask();
};
//------------------------------------------------------

View file

@ -170,3 +170,26 @@ void TFxUtil::setKeyframe(const TFxP &dstFx, int dstFrame, const TFxP &srcFx,
dstParam->assignKeyframe(dstFrame, srcParam, srcFrame, changedOnly);
}
}
//-------------------------------------------------------------------
TFxP TFxUtil::makeMask(const TFxP &source, const TFxP &mask) {
if (!source)
return 0;
else if (!mask)
return source;
assert(source && mask);
TFxP columnMaskFx = TFx::create("columnMaskFx");
if (!columnMaskFx) {
assert(columnMaskFx);
return 0;
}
if (!columnMaskFx->connect("Source", source.getPointer()) ||
!columnMaskFx->connect("Mask", mask.getPointer()))
assert(!"Could not connect ports!");
return columnMaskFx;
}

View file

@ -1172,7 +1172,11 @@ TRenderSettings::TRenderSettings()
, m_isCanceled(NULL)
, m_getFullSizeBBox(false)
, m_linearColorSpace(false)
, m_colorSpaceGamma(2.2) {}
, m_colorSpaceGamma(2.2)
, m_applyMask(false)
, m_invertedMask(false)
, m_useMaskBox(false)
, m_plasticMask(false) {}
//------------------------------------------------------------------------------
@ -1217,7 +1221,9 @@ bool TRenderSettings::operator==(const TRenderSettings &rhs) const {
m_mark != rhs.m_mark || m_isSwatch != rhs.m_isSwatch ||
m_userCachable != rhs.m_userCachable ||
m_linearColorSpace != rhs.m_linearColorSpace ||
m_colorSpaceGamma != rhs.m_colorSpaceGamma)
m_colorSpaceGamma != rhs.m_colorSpaceGamma ||
m_applyMask != rhs.m_applyMask || m_invertedMask != rhs.m_invertedMask ||
m_useMaskBox != rhs.m_useMaskBox || m_plasticMask != rhs.m_plasticMask)
return false;
return std::equal(m_data.begin(), m_data.end(), rhs.m_data.begin(), areEqual);

View file

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
version="1.1"
id="svg892"
sodipodi:docname="clipping_mask.svg"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview894"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="true"
inkscape:snap-global="false"
inkscape:zoom="64"
inkscape:cx="7.5234375"
inkscape:cy="6.765625"
inkscape:window-width="1920"
inkscape:window-height="1141"
inkscape:window-x="-7"
inkscape:window-y="-7"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
units="px"
showguides="false">
<inkscape:grid
type="xygrid"
id="grid965" />
</sodipodi:namedview>
<defs
id="defs889" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
id="path1002"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.0237399;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.26458333,0.26458333 C 0.03981829,0.78329902 0.50884996,1.7192215 1.0583333,1.8520833 1.351223,1.9229023 1.8154174,1.5944483 2.1166667,1.5875 2.4546326,1.5797037 2.8492942,1.9426255 3.175,1.8520833 3.6967679,1.7070394 4.1856711,0.76079233 3.96875,0.26458333 3.5697303,-0.07377827 2.8143091,0.3504186 2.1166667,0.52916667 1.634165,0.39260468 0.58283798,-0.09177923 0.26458333,0.26458333 Z M 1.3229167,0.79375 c 0.2622239,-8e-8 0.5291668,0.10069343 0.5291666,0.2645833 10e-8,0.1638899 -0.2669428,0.2645835 -0.5291666,0.2645834 -0.2622237,0 -0.52916675,-0.1006936 -0.5291667,-0.2645834 -1.9e-7,-0.16388985 0.2669429,-0.26458329 0.5291667,-0.2645833 z m 1.5875,0 c 0.2622231,1.3e-7 0.529166,0.10069388 0.5291666,0.2645833 1e-7,0.1638897 -0.266943,0.2645833 -0.5291666,0.2645834 -0.2622238,0 -0.5291668,-0.1006935 -0.5291667,-0.2645834 6e-7,-0.16388954 0.2669434,-0.26458334 0.5291667,-0.2645833 z"
sodipodi:nodetypes="ccccccccccccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -598,6 +598,8 @@
<file>icons/dark/actions/16/table.svg</file>
<file>icons/dark/actions/16/motion_path.svg</file>
<file>icons/dark/actions/16/clipping_mask.svg</file>
<!-- Keyframe -->
<file>icons/dark/actions/20/key_off.svg</file>
<file>icons/dark/actions/20/key_on.svg</file>

View file

@ -66,6 +66,7 @@
#include <QCheckBox>
#include <QPushButton>
#include <QDesktopWidget>
#include <QGroupBox>
#include <QBitmap>
@ -186,8 +187,7 @@ class ColumnMaskUndo final : public TUndo {
std::string m_name;
public:
ColumnMaskUndo(int column, bool isMask, std::string name)
: m_col(column), m_isMask(isMask), m_name(name) {}
ColumnMaskUndo(int column, bool isMask) : m_col(column), m_isMask(isMask) {}
~ColumnMaskUndo() {}
void undo() const override {
@ -196,20 +196,6 @@ public:
TXshColumn::ColumnType type = column->getColumnType();
if (type != TXshColumn::eLevelType) return;
if (containsVectorLevel(m_col)) {
column->setIsMask(m_isMask);
TApp::instance()->getCurrentScene()->notifySceneChanged();
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
}
void redo() const override {
TXshColumn *column =
TApp::instance()->getCurrentXsheet()->getXsheet()->getColumn(m_col);
TXshColumn::ColumnType type = column->getColumnType();
if (type != TXshColumn::eLevelType) return;
if (containsVectorLevel(m_col)) {
column->setIsMask(!m_isMask);
TApp::instance()->getCurrentScene()->notifySceneChanged();
@ -218,10 +204,116 @@ public:
}
}
void redo() const override {
TXshColumn *column =
TApp::instance()->getCurrentXsheet()->getXsheet()->getColumn(m_col);
TXshColumn::ColumnType type = column->getColumnType();
if (type != TXshColumn::eLevelType) return;
if (containsVectorLevel(m_col)) {
column->setIsMask(m_isMask);
TApp::instance()->getCurrentScene()->notifySceneChanged();
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
}
int getSize() const override { return sizeof(*this); }
QString getHistoryString() override {
QString str = QObject::tr("Toggle vector column as mask. ");
QString str = QObject::tr("Toggle column as mask. ");
return str;
}
int getHistoryType() override { return HistoryType::Xsheet; }
};
class ColumnMaskInvertUndo final : public TUndo {
int m_col;
bool m_invertMask;
public:
ColumnMaskInvertUndo(int column, bool invertMask)
: m_col(column), m_invertMask(invertMask) {}
~ColumnMaskInvertUndo() {}
void undo() const override {
TXshColumn *column =
TApp::instance()->getCurrentXsheet()->getXsheet()->getColumn(m_col);
TXshColumn::ColumnType type = column->getColumnType();
if (type != TXshColumn::eLevelType) return;
if (containsVectorLevel(m_col)) {
column->setInvertedMask(!m_invertMask);
TApp::instance()->getCurrentScene()->notifySceneChanged();
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
}
void redo() const override {
TXshColumn *column =
TApp::instance()->getCurrentXsheet()->getXsheet()->getColumn(m_col);
TXshColumn::ColumnType type = column->getColumnType();
if (type != TXshColumn::eLevelType) return;
if (containsVectorLevel(m_col)) {
column->setInvertedMask(m_invertMask);
TApp::instance()->getCurrentScene()->notifySceneChanged();
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
}
int getSize() const override { return sizeof(*this); }
QString getHistoryString() override {
QString str = QObject::tr("Toggle invert column mask. ");
return str;
}
int getHistoryType() override { return HistoryType::Xsheet; }
};
class ColumnMaskRenderUndo final : public TUndo {
int m_col;
bool m_renderMask;
public:
ColumnMaskRenderUndo(int column, bool renderMask)
: m_col(column), m_renderMask(renderMask) {}
~ColumnMaskRenderUndo() {}
void undo() const override {
TXshColumn *column =
TApp::instance()->getCurrentXsheet()->getXsheet()->getColumn(m_col);
TXshColumn::ColumnType type = column->getColumnType();
if (type != TXshColumn::eLevelType) return;
if (containsVectorLevel(m_col)) {
column->setCanRenderMask(!m_renderMask);
TApp::instance()->getCurrentScene()->notifySceneChanged();
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
}
void redo() const override {
TXshColumn *column =
TApp::instance()->getCurrentXsheet()->getXsheet()->getColumn(m_col);
TXshColumn::ColumnType type = column->getColumnType();
if (type != TXshColumn::eLevelType) return;
if (containsVectorLevel(m_col)) {
column->setCanRenderMask(m_renderMask);
TApp::instance()->getCurrentScene()->notifySceneChanged();
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
}
int getSize() const override { return sizeof(*this); }
QString getHistoryString() override {
QString str = QObject::tr("Toggle render column mask. ");
return str;
}
int getHistoryType() override { return HistoryType::Xsheet; }
@ -820,8 +912,13 @@ void ColumnArea::DrawHeader::levelColors(QColor &columnColor,
if (usage == Reference) {
columnColor = m_viewer->getReferenceColumnColor();
dragColor = m_viewer->getReferenceColumnBorderColor();
} else
} else {
m_viewer->getColumnColor(columnColor, dragColor, col, xsh);
if (column->isMask() && m_viewer->orientation()->isVerticalTimeline() &&
m_viewer->getXsheetLayout() == "Minimum")
columnColor = columnColor.lighter(175);
}
}
void ColumnArea::DrawHeader::soundColors(QColor &columnColor,
QColor &dragColor) const {
@ -1360,7 +1457,7 @@ void ColumnArea::DrawHeader::drawParentHandleName() const {
void ColumnArea::DrawHeader::drawFilterColor() const {
if (col < 0 || isEmpty || column->getColorFilterId() == 0 ||
column->getSoundColumn() || column->getSoundTextColumn() ||
column->getPaletteColumn())
column->getPaletteColumn() || column->isMask())
return;
TPixel32 filterColor = TApp::instance()
@ -1374,6 +1471,35 @@ void ColumnArea::DrawHeader::drawFilterColor() const {
p.drawPixmap(filterColorRect, getColorChipIcon(filterColor).pixmap(12, 12));
}
void ColumnArea::DrawHeader::drawClippingMask() const {
if (col < 0 || isEmpty || !column->isMask() ||
!o->flag(PredefinedFlag::THUMBNAIL_AREA_VISIBLE))
return;
QColor maskColor = QColor(75, 75, 75);
if (column->getColorFilterId()) {
TPixel32 filterColor =
TApp::instance()
->getCurrentScene()
->getScene()
->getProperties()
->getColorFilterColor(column->getColorFilterId());
maskColor.setRgb(filterColor.r, filterColor.g, filterColor.b,
filterColor.m);
}
static QPixmap basePixmap = generateIconPixmap(
"clipping_mask", qreal(1.0), QSize(), Qt::KeepAspectRatio, false);
ThemeManager &themeManager = ThemeManager::getInstance();
QPixmap maskPixmap = themeManager.recolorBlackPixels(basePixmap, maskColor);
QRect clippingMaskArea =
o->rect(PredefinedRect::CLIPPING_MASK_AREA).translated(orig);
p.drawPixmap(clippingMaskArea, maskPixmap);
}
void ColumnArea::DrawHeader::drawSoundIcon(bool isPlaying) const {
QRect rect = m_viewer->orientation()
->rect(PredefinedRect::SOUND_ICON)
@ -1670,6 +1796,7 @@ void ColumnArea::drawLevelColumnHead(QPainter &p, int col) {
QPixmap iconPixmap = getColumnIcon(col);
drawHeader.drawThumbnail(iconPixmap);
drawHeader.drawFilterColor();
drawHeader.drawClippingMask();
drawHeader.drawConfig();
drawHeader.drawPegbarName();
drawHeader.drawParentHandleName();
@ -1973,7 +2100,8 @@ ColumnTransparencyPopup::ColumnTransparencyPopup(XsheetViewer *viewer,
: QWidget(parent, Qt::Popup)
, m_viewer(viewer)
, m_lockBtn(nullptr)
, m_keepClosed(false) {
, m_keepClosed(false)
, m_keepClosedTimer(0) {
setFixedWidth(8 + 78 + 8 + 100 + 8 + 8 + 8 + 7);
m_keepClosedTimer = new QTimer(this);
@ -1994,6 +2122,25 @@ m_value->setFont(font);*/
// contents of the combo box will be updated in setColumn
m_filterColorCombo = new QComboBox(this);
m_invertMask = new QCheckBox(tr("Invert Mask"), this);
m_invertMask->setCheckable(true);
m_renderMask = new QCheckBox(tr("Render Mask"), this);
m_renderMask->setCheckable(true);
m_maskGroupBox = new QGroupBox("Clipping Mask", this);
m_maskGroupBox->setCheckable(true);
QGridLayout *maskLay = new QGridLayout();
maskLay->setMargin(5);
maskLay->setHorizontalSpacing(6);
maskLay->setVerticalSpacing(6);
maskLay->setColumnStretch(2, 1);
{
maskLay->addWidget(m_invertMask, 0, 0, 1, 2);
maskLay->addWidget(m_renderMask, 1, 0, 1, 2);
}
m_maskGroupBox->setLayout(maskLay);
// Lock button is moved in the popup for Minimum layout
QPushButton *lockExtraBtn = nullptr;
if (m_viewer->getXsheetLayout() == "Minimum") {
@ -2036,6 +2183,8 @@ m_value->setFont(font);*/
mainLayout->addWidget(m_filterColorCombo, 1, 1,
Qt::AlignLeft | Qt::AlignVCenter);
mainLayout->addWidget(m_maskGroupBox, 2, 0, 1, 2);
if (m_lockBtn) {
QHBoxLayout *lockLay = new QHBoxLayout();
lockLay->setMargin(0);
@ -2044,7 +2193,7 @@ m_value->setFont(font);*/
lockLay->addWidget(m_lockBtn, 0);
lockLay->addWidget(lockExtraBtn, 0);
}
mainLayout->addLayout(lockLay, 2, 1, Qt::AlignLeft | Qt::AlignVCenter);
mainLayout->addLayout(lockLay, 3, 1, Qt::AlignLeft | Qt::AlignVCenter);
}
}
setLayout(mainLayout);
@ -2060,6 +2209,14 @@ m_value->setFont(font);*/
ret = ret && connect(m_filterColorCombo, SIGNAL(activated(int)), this,
SLOT(onFilterColorChanged()));
ret = ret && connect(m_maskGroupBox, SIGNAL(clicked(bool)), this,
SLOT(onMaskGroupBoxChanged(bool)));
ret = ret && connect(m_invertMask, SIGNAL(stateChanged(int)), this,
SLOT(onInvertMaskCBChanged(int)));
ret = ret && connect(m_renderMask, SIGNAL(stateChanged(int)), this,
SLOT(onRenderMaskCBChanged(int)));
if (m_lockBtn)
ret = ret && connect(m_lockBtn, SIGNAL(clicked(bool)), this,
SLOT(onLockButtonClicked(bool)));
@ -2128,6 +2285,44 @@ void ColumnTransparencyPopup::onLockButtonClicked(bool on) {
((ColumnArea *)parent())->update();
}
//-----------------------------------------------------------------------------
void ColumnTransparencyPopup::onMaskGroupBoxChanged(bool clicked) {
int col = m_column->getIndex();
ColumnMaskUndo *undo = new ColumnMaskUndo(col, m_maskGroupBox->isChecked());
undo->redo();
TUndoManager::manager()->add(undo);
update();
}
//-----------------------------------------------------------------------------
void ColumnTransparencyPopup::onInvertMaskCBChanged(int checkedState) {
bool checked = checkedState == Qt::Checked;
int col = m_column->getIndex();
ColumnMaskInvertUndo *undo = new ColumnMaskInvertUndo(col, checked);
undo->redo();
TUndoManager::manager()->add(undo);
update();
}
//-----------------------------------------------------------------------------
void ColumnTransparencyPopup::onRenderMaskCBChanged(int checkedState) {
bool checked = checkedState == Qt::Checked;
int col = m_column->getIndex();
ColumnMaskRenderUndo *undo = new ColumnMaskRenderUndo(col, checked);
undo->redo();
TUndoManager::manager()->add(undo);
update();
}
//----------------------------------------------------------------
void ColumnTransparencyPopup::setColumn(TXshColumn *column) {
@ -2160,6 +2355,24 @@ void ColumnTransparencyPopup::setColumn(TXshColumn *column) {
m_filterColorCombo->setCurrentIndex(
m_filterColorCombo->findData(m_column->getColorFilterId()));
m_maskGroupBox->blockSignals(true);
m_invertMask->blockSignals(true);
m_renderMask->blockSignals(true);
if (containsVectorLevel(m_column->getIndex())) {
m_maskGroupBox->setChecked(m_column->isMask());
m_maskGroupBox->setEnabled(true);
m_invertMask->setChecked(m_column->isInvertedMask());
m_renderMask->setChecked(m_column->canRenderMask());
} else {
m_maskGroupBox->setChecked(false);
m_maskGroupBox->setEnabled(false);
m_invertMask->setChecked(false);
m_renderMask->setChecked(false);
}
m_maskGroupBox->blockSignals(false);
m_invertMask->blockSignals(false);
m_renderMask->blockSignals(false);
if (m_lockBtn) m_lockBtn->setChecked(m_column->isLocked());
}
@ -3057,24 +3270,8 @@ void ColumnArea::contextMenuEvent(QContextMenuEvent *event) {
if (!xsh->isColumnEmpty(col)) {
menu.addAction(cmdManager->getAction(MI_ReplaceLevel));
menu.addAction(cmdManager->getAction(MI_ReplaceParentDirectory));
// if (containsVectorLevel(col)) {
// menu.addSeparator();
// QAction *setMask =
// new QAction(tr("Temporary Mask (Not in final render)"), this);
// setMask->setCheckable(true);
// setMask->setChecked(xsh->getColumn(col)->isMask());
// setMask->setToolTip(
// tr("Only Toonz Vector levels can be used as masks. \n Masks don't
// "
// "show up in final renders."));
// bool ret = true;
// ret = ret &&
// connect(setMask, &QAction::toggled, [=]() { onSetMask(col); });
// assert(ret);
// menu.addAction(setMask);
//}
}
if (o->isVerticalTimeline()) {
menu.addSeparator();
QAction *showParentColors =
@ -3122,20 +3319,6 @@ void ColumnArea::contextMenuEvent(QContextMenuEvent *event) {
//-----------------------------------------------------------------------------
void ColumnArea::onSetMask(int col) {
TXshColumn *column = m_viewer->getXsheet()->getColumn(m_col);
std::string name = m_viewer->getXsheet()
->getStageObject(TStageObjectId::ColumnId(col))
->getName();
ColumnMaskUndo *undo = new ColumnMaskUndo(col, column->isMask(), name);
undo->redo();
TUndoManager::manager()->add(undo);
update();
}
//-----------------------------------------------------------------------------
void ColumnArea::onSubSampling(QAction *action) {
int subsampling;
if (action == m_subsampling1)

View file

@ -31,6 +31,8 @@ class QPushButton;
class Orientation;
class TApp;
class TXsheet;
class QCheckBox;
class QGroupBox;
//=============================================================================
namespace XsheetGUI {
@ -222,6 +224,10 @@ class ColumnTransparencyPopup final : public QWidget {
XsheetViewer *m_viewer;
QPushButton *m_lockBtn;
QGroupBox *m_maskGroupBox;
QCheckBox *m_invertMask;
QCheckBox *m_renderMask;
QTimer *m_keepClosedTimer;
bool m_keepClosed;
@ -247,6 +253,10 @@ protected slots:
void onFilterColorChanged();
void onLockButtonClicked(bool on);
void onMaskGroupBoxChanged(bool clicked);
void onInvertMaskCBChanged(int checkedState);
void onRenderMaskCBChanged(int checkedState);
void resetKeepClosed();
};
@ -352,6 +362,7 @@ class ColumnArea final : public QWidget {
void drawPegbarName() const;
void drawParentHandleName() const;
void drawFilterColor() const;
void drawClippingMask() const;
void drawSoundIcon(bool isPlaying) const;
void drawVolumeControl(double volume) const;
@ -401,7 +412,6 @@ protected slots:
void onCameraColumnChangedTriggered();
void onCameraColumnLockToggled(bool);
void onXsheetCameraChange(int);
void onSetMask(int);
};
//-----------------------------------------------------------------------------

View file

@ -151,7 +151,7 @@ void XsheetViewer::getColumnColor(QColor &color, QColor &sideColor, int index,
getCellTypeAndColors(ltype, color, sideColor, cell);
}
}
if (xsh->getColumn(index)->isMask()) color = QColor(255, 0, 255);
// if (xsh->getColumn(index)->isMask()) color = QColor(255, 0, 255);
}
//-----------------------------------------------------------------------------

View file

@ -500,6 +500,9 @@ TopToBottomOrientation::TopToBottomOrientation() {
iconRect(cameraIconArea, ICON_WIDTH, ICON_HEIGHT));
addRect(PredefinedRect::FILTER_COLOR, rect(PredefinedRect::CONFIG));
addRect(PredefinedRect::CLIPPING_MASK_AREA, QRect(0, 0, -1, -1));
addRect(PredefinedRect::SOUND_ICON, QRect(0, 0, -1, -1));
addRect(PredefinedRect::VOLUME_AREA, QRect(0, 0, -1, -1));
addRect(PredefinedRect::PEGBAR_NAME, QRect(0, 0, -1, -1));
@ -596,6 +599,10 @@ TopToBottomOrientation::TopToBottomOrientation() {
addRect(PredefinedRect::FILTER_COLOR,
QRect(thumbnail.right() - 14, thumbnail.top() + 3, 12, 12));
addRect(PredefinedRect::CLIPPING_MASK_AREA,
QRect(thumbnail.right() - ICON_WIDTH, thumbnail.top() + 2, ICON_WIDTH,
ICON_HEIGHT));
addRect(PredefinedRect::SOUND_ICON,
QRect(thumbnailArea.topLeft(), QSize(40, 30))
.adjusted((thumbnailArea.width() / 2) - (40 / 2),
@ -708,6 +715,10 @@ TopToBottomOrientation::TopToBottomOrientation() {
addRect(PredefinedRect::FILTER_COLOR,
QRect(thumbnail.right() - 14, thumbnail.top() + 3, 12, 12));
addRect(PredefinedRect::CLIPPING_MASK_AREA,
QRect(thumbnail.right() - ICON_WIDTH, thumbnail.top() + 2,
ICON_WIDTH, ICON_HEIGHT));
addRect(PredefinedRect::SOUND_ICON,
QRect(thumbnailArea.topLeft(), QSize(40, 30))
.adjusted((thumbnailArea.width() / 2) - (40 / 2),
@ -816,6 +827,10 @@ TopToBottomOrientation::TopToBottomOrientation() {
addRect(PredefinedRect::FILTER_COLOR,
QRect(thumbnail.right() - 14, thumbnail.top() + 3, 12, 12));
addRect(PredefinedRect::CLIPPING_MASK_AREA,
QRect(thumbnail.right() - ICON_WIDTH, thumbnail.top() + 2,
ICON_WIDTH, ICON_HEIGHT));
addRect(
PredefinedRect::SOUND_ICON,
QRect(thumbnailArea.topLeft(), QSize(40, 30)).adjusted(21, 19, 21, 19));
@ -1306,6 +1321,10 @@ LeftToRightOrientation::LeftToRightOrientation() {
addRect(PredefinedRect::FILTER_COLOR,
QRect(thumbnail.right() - 14, thumbnail.top() + 3, 12, 12));
addRect(PredefinedRect::CLIPPING_MASK_AREA,
QRect(thumbnail.right() - 14, thumbnail.top() + 3, 12, 12));
addRect(PredefinedRect::PEGBAR_NAME, QRect(0, 0, -1, -1)); // hide
addRect(PredefinedRect::PARENT_HANDLE_NAME, QRect(0, 0, -1, -1)); // hide

View file

@ -244,6 +244,8 @@ bool PlasticDeformerFx::buildTextureDataSl(double frame, TRenderSettings &info,
TScale(handledAff.a11 / worldLevelToLevelAff.a11) * info.m_affine;
}
info.m_invertedMask = texColumn->isInvertedMask();
return true;
}
@ -272,6 +274,10 @@ void PlasticDeformerFx::doCompute(TTile &tile, double frame,
// Build texture data
TRenderSettings texInfo(info);
texInfo.m_applyMask = false;
texInfo.m_useMaskBox = false;
texInfo.m_plasticMask = info.m_applyMask;
TAffine worldTexLevelToTexLevelAff;
if (dynamic_cast<TLevelColumnFx *>(m_port.getFx())) {
@ -355,6 +361,9 @@ void PlasticDeformerFx::doCompute(TTile &tile, double frame,
TTile inTile;
m_port->allocateAndCompute(inTile, bbox.getP00(), tileSize, TRasterP(), frame,
texInfo);
TTile origTile(tile.getRaster()->clone());
QOpenGLContext *context;
// Draw the textured mesh
{
@ -448,6 +457,13 @@ void PlasticDeformerFx::doCompute(TTile &tile, double frame,
context->moveToThread(0);
context->doneCurrent();
delete context;
if (info.m_applyMask) {
if (texInfo.m_invertedMask)
TRop::ropout(origTile.getRaster(), tile.getRaster(), tile.getRaster());
else
TRop::ropin(origTile.getRaster(), tile.getRaster(), tile.getRaster());
}
}
assert(glGetError() == GL_NO_ERROR);
}

View file

@ -618,6 +618,8 @@ public:
// prevent infinite loop.
QMap<std::wstring, QPair<TFxP, bool>> m_globalControlledFx;
bool m_applyMasks;
public:
FxBuilder(ToonzScene *scene, TXsheet *xsh, double frame, int whichLevels,
bool isPreview = false, bool expandXSheet = true);
@ -648,7 +650,8 @@ FxBuilder::FxBuilder(ToonzScene *scene, TXsheet *xsh, double frame,
, m_whichLevels(whichLevels)
, m_isPreview(isPreview)
, m_expandXSheet(expandXSheet)
, m_particleDescendentCount(0) {
, m_particleDescendentCount(0)
, m_applyMasks(true) {
TStageObjectId cameraId;
if (m_isPreview)
cameraId = m_xsh->getStageObjectTree()->getCurrentPreviewCameraId();
@ -918,8 +921,9 @@ PlacedFx FxBuilder::makePF(TLevelColumnFx *lcfx) {
pf.m_fx = lcfx;
/*-- subXsheetのとき、その中身もBuildFxを実行 --*/
if (!cell.isEmpty() && !cell.getFrameId().isStopFrame() &&
cell.m_level->getChildLevel()) {
bool isSubXsheet = !cell.isEmpty() && !cell.getFrameId().isStopFrame() &&
cell.m_level->getChildLevel();
if (isSubXsheet) {
// Treat the sub-xsheet case - build the sub-render-tree and reassign stuff
// to pf
TXsheet *xsh = cell.m_level->getChildLevel()->getXsheet();
@ -1001,6 +1005,27 @@ PlacedFx FxBuilder::makePF(TLevelColumnFx *lcfx) {
}
}
// Add check for/create all ClippingMaskFx here
if (m_applyMasks && (sl || isSubXsheet) &&
(!column->isMask() || column->canRenderMask())) {
m_applyMasks = false;
std::vector<TXshColumn *> masks = column->getColumnMasks();
for (int i = 0; i < masks.size(); i++) {
TXshLevelColumn *mask = masks[i]->getLevelColumn();
if (!mask) break;
TXshCell maskCell = mask->getCell(m_frame);
if (maskCell.isEmpty() || maskCell.getFrameId() == TFrameId::STOP_FRAME)
continue;
PlacedFx maskPf = makePF(mask->getFx());
maskPf.m_fx = getFxWithColumnMovements(maskPf);
maskPf.m_fx = TFxUtil::makeAffine(maskPf.m_fx, pf.m_aff.inv());
pf.m_fx = TFxUtil::makeMask(pf.m_fx, maskPf.m_fx);
}
m_applyMasks = true;
}
return pf;
} else
return PlacedFx();

View file

@ -38,6 +38,7 @@
#include "imagebuilders.h"
#include "toonz/toonzscene.h"
#include "toonz/sceneproperties.h"
#include "tstencilcontrol.h"
// Qt includes
#include <QImage>
@ -119,19 +120,6 @@ bool descending(int i, int j) { return (i > j); }
//----------------------------------------------------------------
} // namespace
//=============================================================================
/*! The ZPlacement class preserve camera position information.
*/
//=============================================================================
class ZPlacement {
public:
TAffine m_aff;
double m_z;
ZPlacement() : m_aff(), m_z(0) {}
ZPlacement(const TAffine &aff, double z) : m_aff(aff), m_z(z) {}
};
//=============================================================================
/*! The class PlayerLt allows a priority operator for player.
\n Compare zdepth of two players.
@ -373,7 +361,7 @@ void StageBuilder::addCell(PlayerSet &players, ToonzScene *scene, TXsheet *xsh,
TXshLevel *xl = cell.m_level.getPointer();
TXshSimpleLevel *sl = xl ? xl->getSimpleLevel() : 0;
// check the previous row for a stop motion layer
if (!xl) {
if (!xl && !column->isMask()) {
// Get the last populated cell
cell = xsh->getCell(row - 1, col);
xl = cell.m_level.getPointer();
@ -400,10 +388,10 @@ void StageBuilder::addCell(PlayerSet &players, ToonzScene *scene, TXsheet *xsh,
if (!columnBehindCamera) return;
bool storePlayer =
sl || (xl->getChildLevel() &&
locals::applyPlasticDeform(column.getPointer(), m_vs) &&
locals::isMeshDeformed(xsh, pegbar, m_vs));
bool storePlayer = sl || (column->isMask() && column->isInvertedMask()) ||
(xl && xl->getChildLevel() &&
locals::applyPlasticDeform(column.getPointer(), m_vs) &&
locals::isMeshDeformed(xsh, pegbar, m_vs));
if (storePlayer) {
// Build and store a player
@ -430,6 +418,9 @@ void StageBuilder::addCell(PlayerSet &players, ToonzScene *scene, TXsheet *xsh,
player.m_opacity = column->getOpacity();
player.m_filterColor =
scene->getProperties()->getColorFilterColor(column->getColorFilterId());
player.m_isMask = column->isMask();
player.m_isInvertedMask = column->isInvertedMask();
player.m_canRenderMask = column->canRenderMask();
if (m_subXSheetStack.empty()) {
player.m_z = columnZ;
@ -491,7 +482,7 @@ void StageBuilder::addCell(PlayerSet &players, ToonzScene *scene, TXsheet *xsh,
player.m_bingoOrder = 10;
players.push_back(player);
} else if (TXshChildLevel *cl = xl->getChildLevel()) {
} else if (TXshChildLevel *cl = xl ? xl->getChildLevel() : 0) {
int childRow = cell.m_frameId.getNumber() - 1;
TXsheet *childXsheet = cl->getXsheet();
TStageObjectId childCameraId =
@ -687,12 +678,20 @@ void StageBuilder::addFrame(PlayerSet &players, ToonzScene *scene, TXsheet *xsh,
TXshColumn *column = xsh->getColumn(c);
bool isMask = false;
if (column && !column->isEmpty() && !column->getSoundColumn()) {
if (!column->isPreviewVisible() && checkPreviewVisibility) continue;
if (!column->isPreviewVisible() && checkPreviewVisibility) {
if (!isMask && column->getColumnType() != TXshColumn::eMeshType) {
while (m_masks.size() > maskCount) m_masks.pop_back();
}
continue;
}
if (column->isCamstandVisible() ||
includeUnvisible) // se l'"occhietto" non e' chiuso
{
if (column->isMask()) // se e' una maschera (usate solo in tab pro)
{
if (column->canRenderMask())
addCellWithOnionSkin(players, scene, xsh, row, c, level,
subSheetColIndex);
isMask = true;
std::vector<int> saveMasks;
saveMasks.swap(m_masks);
@ -708,7 +707,7 @@ void StageBuilder::addFrame(PlayerSet &players, ToonzScene *scene, TXsheet *xsh,
subSheetColIndex);
}
}
if (!isMask) {
if (!isMask && column->getColumnType() != TXshColumn::eMeshType) {
while (m_masks.size() > maskCount) m_masks.pop_back();
}
}
@ -863,7 +862,13 @@ void StageBuilder::visit(PlayerSet &players, Visitor &visitor, bool isPlaying) {
visit(*m_maskPool[maskIndex], visitor, isPlaying);
visitor.endMask();
masks.push_back(maskIndex);
visitor.enableMask();
TStencilControl::MaskType maskType = TStencilControl::SHOW_INSIDE;
if (m_maskPool[maskIndex]->size()) {
Player playerMask = m_maskPool[maskIndex]->at(0);
if (playerMask.m_isInvertedMask)
maskType = TStencilControl::SHOW_OUTSIDE;
}
visitor.enableMask(maskType);
i++;
}
}

View file

@ -46,7 +46,10 @@ Stage::Player::Player()
, m_isPlaying(false)
, m_opacity(255)
, m_bingoOrder(0)
, m_currentDrawingOnTop(false) {}
, m_currentDrawingOnTop(false)
, m_isMask(false)
, m_isInvertedMask(false)
, m_canRenderMask(false) {}
//-----------------------------------------------------------------------------

View file

@ -327,7 +327,7 @@ void Picker::endMask() {}
//-----------------------------------------------------------------------------
void Picker::enableMask() {}
void Picker::enableMask(TStencilControl::MaskType maskType) {}
//-----------------------------------------------------------------------------
@ -389,8 +389,8 @@ void RasterPainter::endMask() {
TStencilControl::instance()->endMask();
}
//! Utilizzato solo per TAB Pro
void RasterPainter::enableMask() {
TStencilControl::instance()->enableMask(TStencilControl::SHOW_INSIDE);
void RasterPainter::enableMask(TStencilControl::MaskType maskType) {
TStencilControl::instance()->enableMask(maskType);
}
//! Utilizzato solo per TAB Pro
void RasterPainter::disableMask() {
@ -785,7 +785,8 @@ static void drawAutocloses(TVectorImage *vi, TVectorRenderData &rd) {
onToonzImage().
*/
void RasterPainter::onImage(const Stage::Player &player) {
if (m_singleColumnEnabled && !player.m_isCurrentColumn) return;
if (m_singleColumnEnabled && !player.m_isCurrentColumn && !player.m_isMask)
return;
// Attempt Plastic-deformed drawing
// For now generating icons of plastic-deformed image causes crash as
@ -1141,11 +1142,15 @@ OpenGlPainter::OpenGlPainter(const TAffine &viewAff, const TRect &rect,
, m_isViewer(isViewer)
, m_alphaEnabled(alphaEnabled)
, m_paletteHasChanged(false)
, m_minZ(0) {}
, m_minZ(0)
, m_singleColumnEnabled(false) {}
//-----------------------------------------------------------------------------
void OpenGlPainter::onImage(const Stage::Player &player) {
if (m_singleColumnEnabled && !player.m_isCurrentColumn && !player.m_isMask)
return;
if (player.m_z < m_minZ) m_minZ = player.m_z;
glPushAttrib(GL_ALL_ATTRIB_BITS);
@ -1330,8 +1335,8 @@ void OpenGlPainter::endMask() {
--m_maskLevel;
TStencilControl::instance()->endMask();
}
void OpenGlPainter::enableMask() {
TStencilControl::instance()->enableMask(TStencilControl::SHOW_INSIDE);
void OpenGlPainter::enableMask(TStencilControl::MaskType maskType) {
TStencilControl::instance()->enableMask(maskType);
}
void OpenGlPainter::disableMask() {
TStencilControl::instance()->disableMask();

View file

@ -42,14 +42,20 @@
#include "toonz/fill.h"
#include "toonz/tstageobjectid.h"
#include "toonz/tstageobject.h"
#include "toonz/tstageobjecttree.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 "toonz/tcamera.h"
#include "imagebuilders.h"
#include "tstencilcontrol.h"
#include "tvectorgl.h"
#include "toonz/glrasterpainter.h"
// 4.6 compatibility - sandor fxs
#include "toonz4.6/raster.h"
#include "sandor_fxs/blend.h"
@ -985,6 +991,10 @@ void TLevelColumnFx::doCompute(TTile &tile, double frame,
const TRenderSettings &info) {
if (!m_levelColumn) return;
if (m_levelColumn->isMask() && !info.m_applyMask &&
!m_levelColumn->canRenderMask() && !info.m_plasticMask)
return;
// Ensure that a corresponding cell and level exists
int row = (int)frame;
TXshCell cell = m_levelColumn->getCell(row);
@ -1075,7 +1085,36 @@ void TLevelColumnFx::doCompute(TTile &tile, double frame,
if (!m_isCachable) vpalette->mutex()->lock();
vpalette->setFrame((int)frame);
m_offlineContext->draw(vectorImage, rd, true);
if (info.m_applyMask) {
bool initMatrix = true;
rd.m_alphaChannel = false;
TStencilControl *stencil = TStencilControl::instance();
glPushAttrib(GL_ALL_ATTRIB_BITS);
tglEnableBlending(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
stencil->beginMask();
m_offlineContext->drawMask(vectorImage, rd, initMatrix);
stencil->endMask();
TStencilControl::MaskType maskType = m_levelColumn->isInvertedMask()
? TStencilControl::SHOW_OUTSIDE
: TStencilControl::SHOW_INSIDE;
stencil->enableMask(maskType);
TRasterP ras = tile.getRaster();
TAffine tileAff = TTranslation((ras->getLx() / 2), (ras->getLy() / 2)) *
TScale(1.0 / info.m_shrinkX, 1.0 / info.m_shrinkY);
GLRasterPainter::drawRaster(tileAff, tile.getRaster(), true);
glPopAttrib();
stencil->disableMask();
} else
m_offlineContext->draw(vectorImage, rd, true);
vpalette->setFrame(oldFrame);
if (!m_isCachable) vpalette->mutex()->unlock();
@ -1464,6 +1503,17 @@ bool TLevelColumnFx::doGetBBox(double frame, TRectD &bBox,
}
}
if (info.m_useMaskBox) {
TXsheet *xsh = m_levelColumn->getXsheet();
TStageObjectId cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
TStageObject *cameraPegbar = xsh->getStageObject(cameraId);
TCamera *camera = cameraPegbar->getCamera();
TRectD imageBBox(bBox);
double enlargement = camera->getRes().lx-imageBBox.x1;
bBox += imageBBox.enlarge(enlargement * dpi);
}
return true;
}
@ -1565,7 +1615,21 @@ std::string TLevelColumnFx::getAlias(double frame,
rdata += "column_0";
}
return getFxType() + "[" + ::to_string(fp.getWideString()) + "," + rdata +
std::vector<TXshColumn *> masks = m_levelColumn->getColumnMasks();
if (masks.size()) {
std::string maskAlias = "masked";
for (int i = 0; i < masks.size(); i++) {
TXshLevelColumn *mask = masks[i]->getLevelColumn();
if (!mask) break;
if (mask->isInvertedMask()) maskAlias += "inv";
if (mask->canRenderMask()) maskAlias += "render";
break;
}
rdata += maskAlias;
}
return getFxType() + "[" + ::to_string(fp.getWideString()) + "," + rdata +
"]";
}

View file

@ -14,6 +14,7 @@
#include "toonz/toonzscene.h"
#include "toonz/imagemanager.h"
#include "imagebuilders.h"
#include "tstencilcontrol.h"
// TnzCore includes
#include "tpalette.h"
@ -191,15 +192,22 @@ DrawableTextureDataP texture_utils::getTextureData(const TXsheet *xsh,
xsh->getPlacement(xsh->getStageObjectTree()->getCurrentCameraId(), frame);
bbox = (cameraAff.inv() * bbox).enlarge(1.0);
// Render the xsheet on the specified bbox
// Render the xsheet on the specified bbox
bool masked = TStencilControl::instance()->isMaskEnabled();
#ifdef MACOSX
// Must move masks aside when building texture
if (masked) TStencilControl::instance()->stashMask();
xsh->getScene()->renderFrame(tex, frame, xsh, bbox, TAffine());
if (masked) TStencilControl::instance()->restoreMask();
#else
// The call below will change context (I know, it's a shame :( )
TGlContext currentContext = tglGetCurrentContext();
{
tglDoneCurrent(currentContext);
// Must move masks aside when building texture
if (masked) TStencilControl::instance()->stashMask();
xsh->getScene()->renderFrame(tex, frame, xsh, bbox, TAffine());
if (masked) TStencilControl::instance()->restoreMask();
tglMakeCurrent(currentContext);
}
#endif

View file

@ -812,7 +812,7 @@ void ToonzScene::renderFrame(const TRaster32P &ras, int row, const TXsheet *xsh,
#ifdef MACOSX
std::unique_ptr<QOpenGLFramebufferObject> fb(
new QOpenGLFramebufferObject(ras->getLx(), ras->getLy()));
fb->setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
fb->bind();
assert(glGetError() == GL_NO_ERROR);

View file

@ -743,6 +743,18 @@ bool TXshColumn::isMask() const { return (m_status & eMasked) != 0; }
//-----------------------------------------------------------------------------
bool TXshColumn::isInvertedMask() const {
return (m_status & eInvertedMask) != 0;
}
//-----------------------------------------------------------------------------
bool TXshColumn::canRenderMask() const {
return (m_status & eRenderMask) != 0;
}
//-----------------------------------------------------------------------------
void TXshColumn::setIsMask(bool on) {
const int mask = eMasked;
if (on)
@ -753,6 +765,26 @@ void TXshColumn::setIsMask(bool on) {
//-----------------------------------------------------------------------------
void TXshColumn::setInvertedMask(bool on) {
const int mask = eInvertedMask;
if (on)
m_status |= mask;
else
m_status &= ~mask;
}
//-----------------------------------------------------------------------------
void TXshColumn::setCanRenderMask(bool on) {
const int mask = eRenderMask;
if (on)
m_status |= mask;
else
m_status &= ~mask;
}
//-----------------------------------------------------------------------------
void TXshColumn::resetColumnProperties() {
setStatusWord(0);
setOpacity(255);

View file

@ -6,6 +6,7 @@
#include "toonz/tcolumnfxset.h"
#include "toonz/tcolumnfx.h"
#include "toonz/txshleveltypes.h"
#include "toonz/txsheet.h"
#include "tstream.h"
@ -309,4 +310,25 @@ bool TXshLevelColumn::setNumbers(int row, int rowCount,
//-----------------------------------------------------------------------------
std::vector<TXshColumn *> TXshLevelColumn::getColumnMasks() {
std::vector<TXshColumn *> masks;
if (m_index <= 0) return masks;
TXsheet *xsh = getXsheet();
for (int i = m_index - 1; i >= 0; i--) {
TXshColumn *mcol = xsh->getColumn(i);
if (!mcol || mcol->isEmpty()) break;
if (mcol->getColumnType() == TXshColumn::eMeshType)
continue; // ignore mesh levels
if (!mcol->isMask() || !mcol->isPreviewVisible()) break;
masks.push_back(mcol);
}
return masks;
}
//-----------------------------------------------------------------------------
PERSIST_IDENTIFIER(TXshLevelColumn, "levelColumn")

View file

@ -436,7 +436,8 @@ QPixmap convertImageToPixmap(const QImage &image) {
// Load, theme colorize and change opacity of an icon image
QImage generateIconImage(const QString &iconSVGName, qreal opacity,
QSize newSize, Qt::AspectRatioMode aspectRatioMode) {
QSize newSize, Qt::AspectRatioMode aspectRatioMode,
bool useThemeColor) {
static ThemeManager &themeManager = ThemeManager::getInstance();
if (iconSVGName.isEmpty() || !themeManager.hasIcon(iconSVGName)) {
@ -453,7 +454,7 @@ QImage generateIconImage(const QString &iconSVGName, qreal opacity,
QImage image(svgToImage(imgPath, newSize, aspectRatioMode));
// Colorize QImage
image = themeManager.recolorBlackPixels(image);
if (useThemeColor) image = themeManager.recolorBlackPixels(image);
// Change opacity if required
if (opacity != qreal(1.0)) image = adjustImageOpacity(image, opacity);
@ -465,9 +466,10 @@ QImage generateIconImage(const QString &iconSVGName, qreal opacity,
// Load, theme colorize and change opacity of an icon image file
QPixmap generateIconPixmap(const QString &iconSVGName, qreal opacity,
QSize newSize, Qt::AspectRatioMode aspectRatioMode) {
QImage image =
generateIconImage(iconSVGName, opacity, newSize, aspectRatioMode);
QSize newSize, Qt::AspectRatioMode aspectRatioMode,
bool useThemeColor) {
QImage image = generateIconImage(iconSVGName, opacity, newSize,
aspectRatioMode, useThemeColor);
return convertImageToPixmap(image);
}