Merge OT changes as of 8-6 (#116)

This commit is contained in:
Jeremy Bullock 2020-08-06 23:11:19 -07:00 committed by GitHub
parent a3a6f0d74d
commit feb9c4f48f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 247 additions and 38 deletions

View file

@ -832,6 +832,9 @@
<item>"STD_inoAddFx" "Add Ino" </item>
<item>"STD_inoAddFx.opacity" "Opacity" </item>
<item>"STD_inoAddFx.clipping_mask" "Clipping Mask" </item>
<item>"STD_inoAddFx.linear" "Linear Color Space" </item>
<item>"STD_inoAddFx.gamma" "Gamma" </item>
<item>"STD_inoAddFx.premultiplied" "Source is Premultiplied" </item>
<item>"STD_inoColorBurnFx" "Color Burn Ino" </item>
<item>"STD_inoColorBurnFx.opacity" "Opacity" </item>
<item>"STD_inoColorBurnFx.clipping_mask" "Clipping Mask" </item>

Binary file not shown.

View file

@ -0,0 +1,40 @@
Following licenses are for source code ported from other open-source software authored by DWANGO Co., Ltd.
- DWANGO OpenToonz Plugins [https://github.com/opentoonz/dwango_opentoonz_plugins]
- OpenToonz Plugin Utility [https://github.com/opentoonz/opentoonz_plugin_utility]
- - - - - - - - - - - - - - - -
DWANGO OpenToonz Plugins
Copyright (c) 2016, DWANGO Co., Ltd.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- - - - - - - - - - - - - - - -
OpenToonz Plugin Utility
Copyright (c) 2016, DWANGO Co., Ltd.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- - - - - - - - - - - - - - - -

Binary file not shown.

View file

@ -2,5 +2,11 @@
<page name="Add Ino">
<control>opacity</control>
<control>clipping_mask</control>
<separator/>
<control>linear</control>
<vbox modeSensitive="linear" mode="1">
<control>gamma</control>
<control>premultiplied</control>
</vbox>
</page>
</fxlayout>

View file

@ -3,6 +3,48 @@
#include "stdfx.h"
#include "ino_common.h"
//------------------------------------------------------------
// Regarding computation in linear color space mode is based on the "ComposeAdd"
// plugin fx by DWANGO Co., Ltd. The major difference from the original
// "ComposeAdd" is the "Source is premultiplied" option; the semi-transparent
// pixels are un-premultiplied before converting to linear color space. Also
// modified the transfer functions to use standard gamma correction.
//------------------------------------------------------------
namespace {
inline void to_xyz(double *xyz, double const *bgr) {
xyz[0] = 0.6069 * bgr[2] + 0.1735 * bgr[1] + 0.2003 * bgr[0]; // X
xyz[1] = 0.2989 * bgr[2] + 0.5866 * bgr[1] + 0.1145 * bgr[0]; // Y
xyz[2] = 0.0000 * bgr[2] + 0.0661 * bgr[1] + 1.1162 * bgr[0]; // Z
}
inline void to_bgr(double *bgr, double const *xyz) {
bgr[0] = +0.0585 * xyz[0] - 0.1187 * xyz[1] + 0.9017 * xyz[2]; // blue
bgr[1] = -0.9844 * xyz[0] + 1.9985 * xyz[1] - 0.0279 * xyz[2]; // green
bgr[2] = +1.9104 * xyz[0] - 0.5338 * xyz[1] - 0.2891 * xyz[2]; // red
}
// convert sRGB color space to power space
template <typename T = double>
inline T to_linear_color_space(T nonlinear_color, T exposure, T gamma) {
// return -std::log(T(1) - std::pow(nonlinear_color, gamma)) / exposure;
return std::pow(nonlinear_color, gamma) / exposure;
}
// convert power space to sRGB color space
template <typename T = double>
inline T to_nonlinear_color_space(T linear_color, T exposure, T gamma) {
// return std::pow(T(1) - std::exp(-exposure * linear_color), T(1) / gamma);
return std::pow(linear_color * exposure, T(1) / gamma);
}
template <class T = double>
const T &clamp(const T &v, const T &lo, const T &hi) {
assert(!(hi < lo));
return (v < lo) ? lo : (hi < v) ? hi : v;
}
} // namespace
/* tnzbase --> Source Files --> tfx --> binaryFx.cppを参照 */
class ino_blend_add final : public TBlendForeBackRasterFx {
FX_PLUGIN_DECLARATION(ino_blend_add)
@ -11,13 +53,30 @@ class ino_blend_add final : public TBlendForeBackRasterFx {
TDoubleParamP m_opacity;
TBoolParamP m_clipping_mask;
TBoolParamP m_linear;
TDoubleParamP m_gamma;
// If the pixel is premultiplied, divide color data by the alpha before
// converting from the colorspace, and then multiply by the alpha afterwards.
// This will correct the color of the semi-transparent pixels in most cases.
TBoolParamP m_premultiplied;
public:
ino_blend_add() : m_opacity(1.0 * ino::param_range()), m_clipping_mask(true) {
ino_blend_add()
: m_opacity(1.0 * ino::param_range())
, m_clipping_mask(true)
, m_linear(false)
, m_gamma(2.2)
, m_premultiplied(true) {
addInputPort("Fore", this->m_up);
addInputPort("Back", this->m_down);
bindParam(this, "opacity", this->m_opacity);
bindParam(this, "clipping_mask", this->m_clipping_mask);
bindParam(this, "linear", this->m_linear);
bindParam(this, "gamma", this->m_gamma);
bindParam(this, "premultiplied", this->m_premultiplied);
this->m_opacity->setValueRange(0, 10.0 * ino::param_range());
this->m_gamma->setValueRange(0.2, 5.0);
}
~ino_blend_add() {}
bool canHandle(const TRenderSettings &rs, double frame) override {
@ -80,7 +139,7 @@ void makeRectCoherent(TRectD &rect, const TPointD &pos) {
rect.y1 = tceil(rect.y1);
rect += pos;
}
}
} // namespace
void ino_blend_add::computeUpAndDown(TTile &tile, double frame,
const TRenderSettings &rs,
TRasterP &dn_ras, TRasterP &up_ras,
@ -240,8 +299,90 @@ void tmpl_(TRasterPT<T> dn_ras_out, const TRasterPT<T> &up_ras,
}
}
}
template <class T, class Q>
void linearAdd(TRasterPT<T> dn_ras_out, const TRasterPT<T> &up_ras,
const double up_opacity, const bool clipping_mask_sw,
const double gamma, const bool premultiplied_sw) {
double maxi = static_cast<double>(T::maxChannelValue); // 255or65535
double limit = (maxi + 0.5) / (maxi + 1.0);
assert(dn_ras_out->getSize() == up_ras->getSize());
for (int yy = 0; yy < dn_ras_out->getLy(); ++yy) {
T *out_pix = dn_ras_out->pixels(yy);
const T *const out_end = out_pix + dn_ras_out->getLx();
const T *up_pix = up_ras->pixels(yy);
for (; out_pix < out_end; ++out_pix, ++up_pix) {
if (up_pix->m <= 0 || up_opacity <= 0) {
continue;
}
double dna = static_cast<double>(out_pix->m) / maxi;
double tmp_opacity = clipping_mask_sw ? up_opacity * dna : up_opacity;
if (tmp_opacity <= 0) continue;
double dnBGR[3];
dnBGR[0] = static_cast<double>(out_pix->b) / maxi;
dnBGR[1] = static_cast<double>(out_pix->g) / maxi;
dnBGR[2] = static_cast<double>(out_pix->r) / maxi;
double dnXYZ[3] = {0.0, 0.0, 0.0};
if (dna > 0.0) {
for (int c = 0; c < 3; c++) {
if (premultiplied_sw)
dnBGR[c] = to_linear_color_space(dnBGR[c] / dna, 1.0, gamma) * dna;
else
dnBGR[c] = to_linear_color_space(dnBGR[c], 1.0, gamma);
}
to_xyz(dnXYZ, dnBGR);
}
double exposure = 1.0 / tmp_opacity;
double upBGR[3];
upBGR[0] = static_cast<double>(up_pix->b) / maxi;
upBGR[1] = static_cast<double>(up_pix->g) / maxi;
upBGR[2] = static_cast<double>(up_pix->r) / maxi;
double upa = static_cast<double>(up_pix->m) / maxi;
for (int c = 0; c < 3; c++) {
if (premultiplied_sw)
upBGR[c] =
to_linear_color_space(upBGR[c] / upa, exposure, gamma) * upa;
else
upBGR[c] = to_linear_color_space(upBGR[c], exposure, gamma);
}
double upXYZ[3];
to_xyz(upXYZ, upBGR);
for (int c = 0; c < 3; c++) dnXYZ[c] += upXYZ[c];
to_bgr(dnBGR, dnXYZ);
// just do over-composite for alpha channel
dna = upa * tmp_opacity + dna * (1.0 - upa * tmp_opacity);
dna = clamp(dna, 0.0, 1.0);
// premultiply the result
double nonlinear_b =
to_nonlinear_color_space(dnBGR[0] / dna, 1.0, gamma) * dna;
double nonlinear_g =
to_nonlinear_color_space(dnBGR[1] / dna, 1.0, gamma) * dna;
double nonlinear_r =
to_nonlinear_color_space(dnBGR[2] / dna, 1.0, gamma) * dna;
out_pix->r =
static_cast<Q>(clamp(nonlinear_r, 0.0, 1.0) * (maxi + 0.999999));
out_pix->g =
static_cast<Q>(clamp(nonlinear_g, 0.0, 1.0) * (maxi + 0.999999));
out_pix->b =
static_cast<Q>(clamp(nonlinear_b, 0.0, 1.0) * (maxi + 0.999999));
out_pix->m = static_cast<Q>(dna * (maxi + 0.999999));
}
}
}
void fx_(TRasterP &dn_ras_out, const TRasterP &up_ras, const TPoint &pos,
const double up_opacity, const bool clipping_mask_sw) {
const double up_opacity, const bool clipping_mask_sw,
const bool linear_sw, const double gamma,
const bool premultiplied_sw) {
/* 交差したエリアを処理するようにする、いるのか??? */
TRect outRect(dn_ras_out->getBounds());
TRect upRect(up_ras->getBounds() + pos);
@ -256,14 +397,22 @@ void fx_(TRasterP &dn_ras_out, const TRasterP &up_ras, const TPoint &pos,
TRaster64P rout64 = cRout, rup64 = cRup;
if (rout32 && rup32) {
tmpl_<TPixel32, UCHAR>(rout32, rup32, up_opacity, clipping_mask_sw);
if (linear_sw)
linearAdd<TPixel32, UCHAR>(rout32, rup32, up_opacity, clipping_mask_sw,
gamma, premultiplied_sw);
else
tmpl_<TPixel32, UCHAR>(rout32, rup32, up_opacity, clipping_mask_sw);
} else if (rout64 && rup64) {
tmpl_<TPixel64, USHORT>(rout64, rup64, up_opacity, clipping_mask_sw);
if (linear_sw)
linearAdd<TPixel64, USHORT>(rout64, rup64, up_opacity, clipping_mask_sw,
gamma, premultiplied_sw);
else
tmpl_<TPixel64, USHORT>(rout64, rup64, up_opacity, clipping_mask_sw);
} else {
throw TRopException("unsupported pixel type");
}
}
}
} // namespace
void ino_blend_add::doCompute(TTile &tile, double frame,
const TRenderSettings &rs) {
/* ------ 画像生成 ---------------------------------------- */
@ -275,6 +424,7 @@ void ino_blend_add::doCompute(TTile &tile, double frame,
/* ------ 動作パラメータを得る ---------------------------- */
const double up_opacity =
this->m_opacity->getValue(frame) / ino::param_range();
const double gamma = this->m_gamma->getValue(frame);
/* ------ (app_begin)log記憶 ------------------------------ */
const bool log_sw = ino::log_enable_sw();
@ -296,8 +446,8 @@ void ino_blend_add::doCompute(TTile &tile, double frame,
if (up_ras) {
up_ras->lock();
}
fx_(dn_ras, up_ras, TPoint(), up_opacity,
this->m_clipping_mask->getValue());
fx_(dn_ras, up_ras, TPoint(), up_opacity, this->m_clipping_mask->getValue(),
this->m_linear->getValue(), gamma, this->m_premultiplied->getValue());
if (up_ras) {
up_ras->unlock();
}

View file

@ -1860,6 +1860,11 @@ void FillTool::leftButtonDown(const TPointD &pos, const TMouseEvent &e) {
// drawCross(m_firstPoint, 6);
invalidate();
} else {
// When using tablet on windows, the mouse press event may be called AFTER
// tablet release. It causes unwanted another "first click" just after
// frame-range-filling. Calling processEvents() here to make sure to
// consume the mouse press event in advance.
qApp->processEvents();
// SECONDO CLICK
TFrameId fid = getCurrentFid();
MultiFiller filler(m_firstPoint, pos, params,

View file

@ -297,9 +297,9 @@ void invalidateIcons() {
s.m_paintIndex = mask & ToonzCheck::ePaint ? tc->getColorIndex() : -1;
IconGenerator::instance()->setSettings(s);
// Force icons to refresh
// Force icons to refresh for Toonz Vector levels
TXshLevel *sl = TApp::instance()->getCurrentLevel()->getLevel();
if (sl) {
if (sl && sl->getType() == PLI_XSHLEVEL) {
std::vector<TFrameId> fids;
sl->getFids(fids);

View file

@ -631,6 +631,10 @@ void TApp::onPaletteChanged() { m_currentScene->setDirtyFlag(true); }
//-----------------------------------------------------------------------------
void TApp::onLevelColorStyleSwitched() {
TXshLevel *sl = m_currentLevel->getLevel();
if (!sl || (sl->getType() != TZP_XSHLEVEL && sl->getType() != PLI_XSHLEVEL))
return;
TPaletteHandle *ph = m_paletteController->getCurrentLevelPalette();
assert(ph);
@ -648,14 +652,12 @@ void TApp::onLevelColorStyleSwitched() {
IconGenerator::instance()->setSettings(s);
TXshLevel *sl = m_currentLevel->getLevel();
if (!sl) return;
std::vector<TFrameId> fids;
sl->getFids(fids);
for (int i = 0; i < (int)fids.size(); i++)
IconGenerator::instance()->invalidate(sl, fids[i]);
if (sl->getType() == PLI_XSHLEVEL) {
std::vector<TFrameId> fids;
sl->getFids(fids);
for (int i = 0; i < (int)fids.size(); i++)
IconGenerator::instance()->invalidate(sl, fids[i]);
}
m_currentLevel->notifyLevelViewChange();
}

View file

@ -149,6 +149,8 @@ void TPaletteHandle::setPalette(TPalette *palette, int styleIndex) {
m_styleParamIndex = 0;
emit paletteSwitched();
// to let ToonzCheck to update the current index
emit broadcastColorStyleSwitched();
}
}

View file

@ -102,8 +102,8 @@ bool checkCreatorString(const QString &creator) {
if (pos >= 0 && len >= 4) {
QString v;
if (len > 4) v = creator.mid(pos + 3, len - 4);
bool ok = true;
mask = v.toInt(&ok, 16);
bool ok = true;
mask = v.toInt(&ok, 16);
}
}
return (mask & compatibility.neededMask) == compatibility.neededMask &&
@ -169,8 +169,8 @@ void getIndexesRangefromFids(TXshSimpleLevel *level,
std::set<TFrameId>::const_iterator it;
for (it = fids.begin(); it != fids.end(); ++it) {
int index = level->guessIndex(*it);
if (index > toIndex) toIndex = index;
int index = level->guessIndex(*it);
if (index > toIndex) toIndex = index;
if (index < fromIndex) fromIndex = index;
}
}
@ -917,13 +917,13 @@ void TXshSimpleLevel::loadData(TIStream &is) {
if (is.getTagParam("dpix", v)) xdpi = std::stod(v);
if (is.getTagParam("dpiy", v)) ydpi = std::stod(v);
if (xdpi != 0 && ydpi != 0) dpiPolicy = LevelProperties::DP_CustomDpi;
std::string dpiType = is.getTagAttribute("dpiType");
if (dpiType == "image") dpiPolicy = LevelProperties::DP_ImageDpi;
if (is.getTagParam("type", v) && v == "s") type = TZI_XSHLEVEL;
if (is.getTagParam("subsampling", v)) subsampling = std::stoi(v);
if (is.getTagParam("premultiply", v)) doPremultiply = std::stoi(v);
std::string dpiType = is.getTagAttribute("dpiType");
if (dpiType == "image") dpiPolicy = LevelProperties::DP_ImageDpi;
if (is.getTagParam("type", v) && v == "s") type = TZI_XSHLEVEL;
if (is.getTagParam("subsampling", v)) subsampling = std::stoi(v);
if (is.getTagParam("premultiply", v)) doPremultiply = std::stoi(v);
if (is.getTagParam("antialias", v)) antialiasSoftness = std::stoi(v);
if (is.getTagParam("whiteTransp", v)) whiteTransp = std::stoi(v);
if (is.getTagParam("whiteTransp", v)) whiteTransp = std::stoi(v);
if (is.getTagParam("isStopMotionLevel", v))
isStopMotionLevel = std::stoi(v);
@ -1094,8 +1094,9 @@ static TFilePath getLevelPathAndSetNameWithPsdLevelName(
if (removeFileName) wLevelName = list[1].toStdWString();
TLevelSet *levelSet = xshLevel->getScene()->getLevelSet();
if (levelSet && levelSet->hasLevel(
wLevelName)) // levelSet should be asserted instead
if (levelSet &&
levelSet->hasLevel(
wLevelName)) // levelSet should be asserted instead
levelSet->renameLevel(xshLevel, wLevelName);
xshLevel->setName(wLevelName);
@ -1653,8 +1654,8 @@ void TXshSimpleLevel::saveSimpleLevel(const TFilePath &decodedFp,
std::vector<TFrameId> fids;
getFids(fids);
bool isLevelModified = getProperties()->getDirtyFlag();
bool isPaletteModified = false;
bool isLevelModified = getProperties()->getDirtyFlag();
bool isPaletteModified = false;
if (getPalette()) isPaletteModified = getPalette()->getDirtyFlag();
if (isLevelModified || isPaletteModified) {
@ -1665,6 +1666,7 @@ void TXshSimpleLevel::saveSimpleLevel(const TFilePath &decodedFp,
if (TSystem::doesExistFileOrLevel(decodedFp)) {
TLevelReaderP lr(decodedFp);
lr->doReadPalette(false);
const TImageInfo *imageInfo = m_frames.empty()
? lr->getImageInfo()
: lr->getImageInfo(*(m_frames.begin()));
@ -1900,11 +1902,11 @@ void TXshSimpleLevel::saveSimpleLevel(const TFilePath &decodedFp,
std::string TXshSimpleLevel::getImageId(const TFrameId &fid,
int frameStatus) const {
if (frameStatus < 0) frameStatus = getFrameStatus(fid);
std::string prefix = "L";
std::string prefix = "L";
if (frameStatus & CleanupPreview)
prefix = "P";
else if ((frameStatus & (Scanned | Cleanupped)) == Scanned)
prefix = "S";
prefix = "S";
std::string imageId = m_idBase + "_" + prefix + fid.expand();
return imageId;
}
@ -2330,14 +2332,13 @@ TFilePath TXshSimpleLevel::getExistingHookFile(
int f, fCount = hookFiles.size();
for (f = 0; f != fCount; ++f) {
fPattern = locals::getPattern(hookFiles[f]);
fPattern = locals::getPattern(hookFiles[f]);
if (fPattern < p) p = fPattern, h = f;
}
assert(h >= 0);
return (h < 0) ? TFilePath()
: decodedLevelPath.getParentDir() +
TFilePath(hookFiles[h].toStdWString());
return (h < 0) ? TFilePath() : decodedLevelPath.getParentDir() +
TFilePath(hookFiles[h].toStdWString());
}
//-----------------------------------------------------------------------------