Merge pull request #1142 from manongjohn/clipping_mask_column
Clipping Mask Column
This commit is contained in:
commit
32a45890bb
|
@ -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")
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -118,6 +118,10 @@ public:
|
|||
|
||||
bool m_currentDrawingOnTop;
|
||||
|
||||
bool m_isMask;
|
||||
bool m_isInvertedMask;
|
||||
bool m_canRenderMask;
|
||||
|
||||
public:
|
||||
Player();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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 &);
|
||||
|
|
|
@ -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);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -167,6 +167,11 @@ public:
|
|||
|
||||
double m_colorSpaceGamma;
|
||||
|
||||
bool m_applyMask;
|
||||
bool m_invertedMask;
|
||||
bool m_useMaskBox;
|
||||
bool m_plasticMask;
|
||||
|
||||
public:
|
||||
TRenderSettings();
|
||||
~TRenderSettings();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
||||
//------------------------------------------------------
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
54
toonz/sources/toonz/icons/dark/actions/16/clipping_mask.svg
Normal file
54
toonz/sources/toonz/icons/dark/actions/16/clipping_mask.svg
Normal 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 |
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 +
|
||||
"]";
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue