TVP JSON export
This commit is contained in:
parent
0b63743b26
commit
51b067b19b
9 changed files with 831 additions and 9 deletions
|
@ -182,6 +182,7 @@ set(HEADERS
|
|||
metnum.h
|
||||
processor.h
|
||||
predict3d.h
|
||||
tvpjson_io.h
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
|
@ -350,6 +351,7 @@ set(SOURCES
|
|||
xdtsimportpopup.cpp
|
||||
expressionreferencemanager.cpp
|
||||
tooloptionsshortcutinvoker.cpp
|
||||
tvpjson_io.cpp
|
||||
# Tracker file
|
||||
dummyprocessor.cpp
|
||||
metnum.cpp
|
||||
|
|
110
toonz/sources/toonz/icons/dark/mimetypes/60/json_icon.svg
Normal file
110
toonz/sources/toonz/icons/dark/mimetypes/60/json_icon.svg
Normal file
|
@ -0,0 +1,110 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="60px"
|
||||
height="60px"
|
||||
version="1.1"
|
||||
xml:space="preserve"
|
||||
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"
|
||||
id="svg44"
|
||||
sodipodi:docname="json_icon.svg"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"><metadata
|
||||
id="metadata50"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs48">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</defs><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1273"
|
||||
inkscape:window-height="826"
|
||||
id="namedview46"
|
||||
showgrid="false"
|
||||
showguides="false"
|
||||
inkscape:zoom="3.9333333"
|
||||
inkscape:cx="0.69877966"
|
||||
inkscape:cy="48.877998"
|
||||
inkscape:window-x="458"
|
||||
inkscape:window-y="28"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg44" />
|
||||
<rect
|
||||
x="0"
|
||||
y="0"
|
||||
width="60"
|
||||
height="60"
|
||||
style="fill:rgb(184,184,184);fill-opacity:0;"
|
||||
id="rect2" />
|
||||
<path
|
||||
d="M 52,58 H 7 V 2 h 34 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 z"
|
||||
style="fill:#a2e5db;fill-rule:nonzero;fill-opacity:1;stroke:none"
|
||||
id="path4"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="M 40,14 V 2 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 z"
|
||||
style="fill:#cfecef;fill-rule:nonzero;fill-opacity:1"
|
||||
id="path7"
|
||||
inkscape:connector-curvature="0" /><g
|
||||
id="g13"
|
||||
transform="translate(-1,1)">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path11"
|
||||
style="fill-opacity:0.25"
|
||||
d="M 41,13 H 52 V 24 Z" />
|
||||
</g><path
|
||||
d="M 52,58 H 7 V 2 h 34 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 v 1 h 1 z M 40,3 H 8 V 57 H 51 V 14 Z"
|
||||
style="fill:#3a839a;fill-opacity:1"
|
||||
id="path15"
|
||||
inkscape:connector-curvature="0" />
|
||||
<g
|
||||
aria-label="JSON"
|
||||
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
|
||||
id="text884"><path
|
||||
d="m 15.929203,42.828612 h 1.920573 v 6.041667 q 0,1.184896 -0.208334,1.822916 -0.279947,0.833334 -1.015625,1.341146 -0.735677,0.501302 -1.940104,0.501302 -1.41276,0 -2.174479,-0.78776 -0.761718,-0.794271 -0.768229,-2.324219 l 1.816406,-0.208333 q 0.03255,0.820312 0.240886,1.158854 0.3125,0.514323 0.950521,0.514323 0.644531,0 0.911458,-0.364583 0.266927,-0.371094 0.266927,-1.529948 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:13.33333302px;font-family:Arial;-inkscape-font-specification:'Arial Bold'"
|
||||
id="path890" /><path
|
||||
d="m 19.418786,49.267414 1.875,-0.182291 q 0.169271,0.94401 0.683594,1.386718 0.520833,0.442709 1.39974,0.442709 0.930989,0 1.399739,-0.390625 0.475261,-0.397136 0.475261,-0.92448 0,-0.338541 -0.201823,-0.572916 -0.195313,-0.240886 -0.690105,-0.416667 -0.338541,-0.117187 -1.542968,-0.416667 -1.549479,-0.384114 -2.174479,-0.94401 -0.878907,-0.78776 -0.878907,-1.920573 0,-0.729166 0.410157,-1.360677 0.416666,-0.638021 1.191406,-0.970052 0.78125,-0.332031 1.88151,-0.332031 1.796875,0 2.701823,0.78776 0.911458,0.787761 0.957031,2.102865 l -1.927083,0.08463 q -0.123698,-0.735677 -0.533854,-1.054687 -0.403646,-0.325521 -1.217448,-0.325521 -0.839844,0 -1.315104,0.345052 -0.30599,0.221354 -0.30599,0.592448 0,0.338542 0.286459,0.579427 0.364583,0.305989 1.770833,0.638021 1.40625,0.332031 2.076823,0.690104 0.677083,0.351562 1.054687,0.970052 0.384115,0.611979 0.384115,1.516927 0,0.820312 -0.455729,1.536458 -0.455729,0.716146 -1.289063,1.067709 -0.833333,0.345052 -2.076823,0.345052 -1.809895,0 -2.779948,-0.833334 -0.970052,-0.839843 -1.158854,-2.441406 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:13.33333302px;font-family:Arial;-inkscape-font-specification:'Arial Bold'"
|
||||
id="path892" /><path
|
||||
d="m 28.422693,47.659341 q 0,-1.458333 0.436198,-2.447916 0.32552,-0.729167 0.885416,-1.308594 0.566406,-0.579427 1.236979,-0.859375 0.891927,-0.377604 2.057292,-0.377604 2.109375,0 3.372396,1.308594 1.269531,1.308593 1.269531,3.639322 0,2.311198 -1.256511,3.619792 -1.25651,1.302083 -3.359374,1.302083 -2.128907,0 -3.385417,-1.295573 -1.25651,-1.302083 -1.25651,-3.580729 z m 1.985677,-0.0651 q 0,1.621094 0.748698,2.460938 0.748697,0.833333 1.901041,0.833333 1.152344,0 1.888021,-0.826823 0.742187,-0.833333 0.742187,-2.49349 0,-1.640625 -0.722656,-2.447916 -0.716146,-0.807292 -1.907552,-0.807292 -1.191406,0 -1.920573,0.820313 -0.729166,0.813802 -0.729166,2.460937 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:13.33333302px;font-family:Arial;-inkscape-font-specification:'Arial Bold'"
|
||||
id="path894" /><path
|
||||
d="m 39.197431,52.372883 v -9.544271 h 1.875 l 3.90625,6.373698 v -6.373698 h 1.790364 v 9.544271 h -1.933593 l -3.847657,-6.223958 v 6.223958 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:13.33333302px;font-family:Arial;-inkscape-font-specification:'Arial Bold'"
|
||||
id="path896" /></g><g
|
||||
aria-label="{;}"
|
||||
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
|
||||
id="text888"><path
|
||||
d="m 25.14849,36.250741 h -1.5625 q -1.864583,0 -3.03125,-1.041666 -1.15625,-1.03125 -1.15625,-2.989584 v -1.552083 q 0,-1.760417 -0.864583,-2.75 -0.864584,-1 -2.645834,-1 h -0.53125 v -1.625 h 0.53125 q 1.78125,0 2.645834,-0.989584 0.864583,-1 0.864583,-2.760416 v -1.552084 q 0,-1.958333 1.15625,-2.989583 1.166667,-1.041667 3.03125,-1.041667 h 1.5625 v 1.4375 h -1.1875 q -1.416666,0 -2.0625,0.65625 -0.635416,0.65625 -0.635416,2.114584 v 1.822916 q 0,1.447917 -0.802084,2.4375 -0.802083,0.979167 -2.229167,1.552084 v 0.25 q 1.427084,0.572916 2.229167,1.5625 0.802084,0.979167 0.802084,2.427083 v 1.822917 q 0,1.458333 0.635416,2.114583 0.645834,0.65625 2.0625,0.65625 h 1.1875 z"
|
||||
style="font-size:21.33333397px"
|
||||
id="path899" /><path
|
||||
d="m 30.902022,23.500741 h -2.489584 v -2.96875 h 2.489584 z m 0.760416,5.697917 -2.9375,6.822917 h -1.520833 l 1.8125,-6.822917 z"
|
||||
style="font-size:21.33333397px"
|
||||
id="path901" /><path
|
||||
d="m 43.205342,26.917408 h -0.53125 q -1.78125,0 -2.645833,1 -0.864584,0.989583 -0.864584,2.75 v 1.552083 q 0,1.958334 -1.166666,2.989584 -1.156251,1.041666 -3.020834,1.041666 h -1.5625 v -1.4375 h 1.1875 q 1.416667,0 2.052083,-0.65625 0.645834,-0.65625 0.645834,-2.114583 v -1.822917 q 0,-1.447916 0.802083,-2.427083 0.802084,-0.989584 2.229167,-1.5625 v -0.25 q -1.427083,-0.572917 -2.229167,-1.552084 -0.802083,-0.989583 -0.802083,-2.4375 v -1.822916 q 0,-1.458334 -0.645834,-2.114584 -0.635416,-0.65625 -2.052083,-0.65625 h -1.1875 v -1.4375 h 1.5625 q 1.864583,0 3.020834,1.041667 1.166666,1.03125 1.166666,2.989583 v 1.552084 q 0,1.760416 0.864584,2.760416 0.864583,0.989584 2.645833,0.989584 h 0.53125 z"
|
||||
style="font-size:21.33333397px"
|
||||
id="path903" /></g></svg>
|
After Width: | Height: | Size: 7.7 KiB |
|
@ -1789,6 +1789,9 @@ void MainWindow::defineActions() {
|
|||
QT_TRANSLATE_NOOP("MainWindow",
|
||||
"Export Exchange Digital Time Sheet (XDTS)"),
|
||||
"");
|
||||
createMenuFileAction(
|
||||
MI_ExportTvpJson,
|
||||
QT_TRANSLATE_NOOP("MainWindow", "Export TVPaint JSON File"), "");
|
||||
createMenuFileAction("MI_RunScript", QT_TR_NOOP("Run Script..."), "",
|
||||
"run_script", tr("Run a script to perform a series of actions on a scene."));
|
||||
createMenuFileAction("MI_OpenScriptConsole",
|
||||
|
|
|
@ -345,6 +345,7 @@ void TopBar::loadMenubar() {
|
|||
addMenuItem(exportMenu, MI_SoundTrack);
|
||||
addMenuItem(exportMenu, MI_ExportXDTS);
|
||||
addMenuItem(exportMenu, MI_StopMotionExportImageSequence);
|
||||
addMenuItem(exportMenu, MI_ExportTvpJson);
|
||||
}
|
||||
fileMenu->addSeparator();
|
||||
addMenuItem(fileMenu, MI_PrintXsheet);
|
||||
|
|
|
@ -468,6 +468,7 @@
|
|||
#define MI_FlipPrevGuideStroke "MI_FlipPrevGuideStroke"
|
||||
|
||||
#define MI_ExportXDTS "MI_ExportXDTS"
|
||||
#define MI_ExportTvpJson "MI_ExportTvpJson"
|
||||
|
||||
#define MI_ToggleAutoCreate "MI_ToggleAutoCreate"
|
||||
#define MI_ToggleCreationInHoldCells "MI_ToggleCreationInHoldCells"
|
||||
|
|
|
@ -174,6 +174,7 @@
|
|||
<file>icons/dark/mimetypes/60/audio_icon.svg</file>
|
||||
<file>icons/dark/mimetypes/60/script_icon.svg</file>
|
||||
<file>icons/dark/mimetypes/60/broken_icon.svg</file>
|
||||
<file>icons/dark/mimetypes/60/json_icon.svg</file>
|
||||
|
||||
<!-- Render -->
|
||||
<file>icons/dark/actions/16/render.svg</file>
|
||||
|
|
516
toonz/sources/toonz/tvpjson_io.cpp
Normal file
516
toonz/sources/toonz/tvpjson_io.cpp
Normal file
|
@ -0,0 +1,516 @@
|
|||
#include "tvpjson_io.h"
|
||||
|
||||
#include "tsystem.h"
|
||||
#include "toonz/toonzscene.h"
|
||||
#include "toonz/tproject.h"
|
||||
#include "toonz/levelset.h"
|
||||
#include "toonz/txsheet.h"
|
||||
#include "toonz/txshcell.h"
|
||||
#include "toonz/txshsimplelevel.h"
|
||||
#include "toonz/txshchildlevel.h"
|
||||
#include "toonz/txsheethandle.h"
|
||||
#include "toonz/tscenehandle.h"
|
||||
#include "toonz/preferences.h"
|
||||
#include "toonz/sceneproperties.h"
|
||||
#include "toonz/tstageobject.h"
|
||||
#include "toutputproperties.h"
|
||||
#include "toonz/tstageobjecttree.h"
|
||||
#include "toonz/tcamera.h"
|
||||
|
||||
#include "toonzqt/menubarcommand.h"
|
||||
#include "toonzqt/gutil.h"
|
||||
|
||||
#include "tapp.h"
|
||||
#include "menubarcommandids.h"
|
||||
#include "filebrowserpopup.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QDesktopServices>
|
||||
#include <QPair>
|
||||
|
||||
using namespace TvpJsonIo;
|
||||
|
||||
namespace {
|
||||
|
||||
QMap<QString, BlendMode> BlendingMatch = {{"Color", BlendMode_Color},
|
||||
{"Darken", BlendMode_Darken}};
|
||||
|
||||
QString getFrameNumberWithLetters(int frame) {
|
||||
int letterNum = frame % 10;
|
||||
QChar letter;
|
||||
|
||||
switch (letterNum) {
|
||||
case 0:
|
||||
letter = QChar();
|
||||
break;
|
||||
case 1:
|
||||
letter = 'A';
|
||||
break;
|
||||
case 2:
|
||||
letter = 'B';
|
||||
break;
|
||||
case 3:
|
||||
letter = 'C';
|
||||
break;
|
||||
case 4:
|
||||
letter = 'D';
|
||||
break;
|
||||
case 5:
|
||||
letter = 'E';
|
||||
break;
|
||||
case 6:
|
||||
letter = 'F';
|
||||
break;
|
||||
case 7:
|
||||
letter = 'G';
|
||||
break;
|
||||
case 8:
|
||||
letter = 'H';
|
||||
break;
|
||||
case 9:
|
||||
letter = 'I';
|
||||
break;
|
||||
default:
|
||||
letter = QChar();
|
||||
break;
|
||||
}
|
||||
|
||||
QString number;
|
||||
if (frame >= 10) {
|
||||
number = QString::number(frame);
|
||||
number.chop(1);
|
||||
} else
|
||||
number = "0";
|
||||
|
||||
return (QChar(letter).isNull()) ? number : number.append(letter);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void TvpJsonLayerLinkItem::read(const QJsonObject& json) {
|
||||
m_instance_index = json["instance-index"].toInt();
|
||||
m_instance_name = json["instance-name"].toString();
|
||||
m_file = json["file"].toString();
|
||||
QJsonArray imagesArray = json["images"].toArray();
|
||||
for (int imageIndex = 0; imageIndex < imagesArray.size(); imageIndex++) {
|
||||
m_images.append(imagesArray[imageIndex].toInt());
|
||||
}
|
||||
}
|
||||
|
||||
void TvpJsonLayerLinkItem::write(QJsonObject& json) const {
|
||||
json["instance-index"] = m_instance_index;
|
||||
json["instance-name"] = m_instance_name;
|
||||
json["file"] = m_file;
|
||||
QJsonArray imagesArray;
|
||||
for (const auto frame : m_images) {
|
||||
imagesArray.append(frame);
|
||||
}
|
||||
json["images"] = imagesArray;
|
||||
}
|
||||
|
||||
void TvpJsonLayerLinkItem::build(int instance_index, QString instance_name,
|
||||
QString file, QList<int> images) {
|
||||
m_instance_index = instance_index;
|
||||
m_instance_name = instance_name;
|
||||
m_file = file;
|
||||
m_images = images;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void TvpJsonLayer::read(const QJsonObject& json) {
|
||||
m_name = json["name"].toString();
|
||||
m_visible = json["visible"].toBool();
|
||||
m_position = json["position"].toInt();
|
||||
m_opacity = json["opacity"].toInt();
|
||||
m_start = json["start"].toInt();
|
||||
m_end = json["end"].toInt();
|
||||
|
||||
QString blendModeName = json["blending-mode"].toString("Color");
|
||||
m_blending_mode = BlendingMatch.value(blendModeName, BlendMode_Color);
|
||||
|
||||
QJsonArray linkArray = json["link"].toArray();
|
||||
for (int linkIndex = 0; linkIndex < linkArray.size(); linkIndex++) {
|
||||
QJsonObject linkItemObject = linkArray[linkIndex].toObject();
|
||||
TvpJsonLayerLinkItem linkItem;
|
||||
linkItem.read(linkItemObject);
|
||||
m_link.append(linkItem);
|
||||
}
|
||||
}
|
||||
|
||||
void TvpJsonLayer::write(QJsonObject& json) const {
|
||||
json["name"] = m_name;
|
||||
json["visible"] = m_visible;
|
||||
json["position"] = m_position;
|
||||
json["opacity"] = m_opacity;
|
||||
json["start"] = m_start;
|
||||
json["end"] = m_end;
|
||||
|
||||
QString blendModeName = BlendingMatch.key(m_blending_mode, "Color");
|
||||
json["blending-mode"] = blendModeName;
|
||||
|
||||
QJsonArray linkArray;
|
||||
for (const auto linkItem : m_link) {
|
||||
QJsonObject linkItemObject;
|
||||
linkItem.write(linkItemObject);
|
||||
linkArray.append(linkItemObject);
|
||||
}
|
||||
json["link"] = linkArray;
|
||||
}
|
||||
|
||||
void TvpJsonLayer::build(int index, ToonzScene* scene, TXshCellColumn* column) {
|
||||
m_position = index;
|
||||
column->getRange(m_start, m_end);
|
||||
|
||||
TXshCell prevCell;
|
||||
|
||||
TFilePath sceneFolder =
|
||||
scene->decodeFilePath(scene->getScenePath().getParentDir());
|
||||
|
||||
// JSONに記入可能かどうかチェック済みのレベル
|
||||
QMap<TXshLevel*, TFilePath> checkedLevels;
|
||||
// register the firstly-found level
|
||||
TXshLevel* firstFoundLevel = nullptr;
|
||||
|
||||
QList<TXshCell> cellList;
|
||||
QList<QList<int>> imgsList;
|
||||
QMap<TXshLevel*, QPair<int, char>> frameFormats;
|
||||
|
||||
for (int row = m_start; row <= m_end; row++) {
|
||||
TXshCell cell = column->getCell(row);
|
||||
// continue if the cell is continuous
|
||||
if (prevCell == cell) continue;
|
||||
|
||||
// try to register the level.
|
||||
TXshLevel* level = cell.getSimpleLevel();
|
||||
if (!level)
|
||||
cell = TXshCell();
|
||||
else if (checkedLevels.contains(level)) {
|
||||
if (checkedLevels[level].isEmpty())
|
||||
cell = TXshCell();
|
||||
else {
|
||||
} // do nothing
|
||||
} else // newly found level
|
||||
{
|
||||
// Only raster levels are exported
|
||||
// currently, only the files located under the scene folder will be
|
||||
// exported
|
||||
if (level->getType() != OVL_XSHLEVEL)
|
||||
cell = TXshCell();
|
||||
else {
|
||||
TFilePath levelFullPath = scene->decodeFilePath(level->getPath());
|
||||
if (sceneFolder.isAncestorOf(levelFullPath)) {
|
||||
checkedLevels[level] = levelFullPath - sceneFolder;
|
||||
if (!firstFoundLevel) firstFoundLevel = level;
|
||||
std::vector<TFrameId> fids;
|
||||
level->getFids(fids);
|
||||
frameFormats[level] = QPair<int, char>(fids[0].getZeroPadding(),
|
||||
fids[0].getStartSeqInd());
|
||||
} else
|
||||
cell = TXshCell();
|
||||
}
|
||||
if (cell.isEmpty()) checkedLevels[level] = TFilePath();
|
||||
}
|
||||
|
||||
// continue if the cell is continuous
|
||||
if (prevCell == cell) continue;
|
||||
|
||||
if (!cellList.contains(cell)) {
|
||||
cellList.append(cell);
|
||||
imgsList.append(QList<int>());
|
||||
}
|
||||
imgsList[cellList.indexOf(cell)].append(row);
|
||||
|
||||
prevCell = cell;
|
||||
}
|
||||
|
||||
if (imgsList.isEmpty()) return;
|
||||
|
||||
//-------
|
||||
|
||||
for (int i = 0; i < imgsList.size(); ++i) {
|
||||
TXshCell cell = cellList[i];
|
||||
|
||||
int instance_index = imgsList[i].at(0);
|
||||
|
||||
QString instance_name;
|
||||
QString file;
|
||||
if (cell.isEmpty()) { // 空コマの場合
|
||||
instance_name = QString::number(0);
|
||||
TFrameId zeroFid(0, 0, frameFormats[firstFoundLevel].first,
|
||||
frameFormats[firstFoundLevel].second);
|
||||
TFilePath imgPath = checkedLevels[firstFoundLevel].withFrame(zeroFid);
|
||||
file = imgPath.getQString();
|
||||
} else {
|
||||
TFrameId fid = cell.getFrameId();
|
||||
|
||||
// convert the last one digit of the frame number to alphabet
|
||||
// Ex. 12 -> 1B 21 -> 2A 30 -> 3
|
||||
if (Preferences::instance()->isShowFrameNumberWithLettersEnabled())
|
||||
instance_name = getFrameNumberWithLetters(fid.getNumber());
|
||||
else {
|
||||
std::string frameNumber("");
|
||||
// set number
|
||||
if (fid.getNumber() >= 0) frameNumber = std::to_string(fid.getNumber());
|
||||
// add letter
|
||||
if (fid.getLetter() != 0) frameNumber.append(1, fid.getLetter());
|
||||
instance_name = QString::fromStdString(frameNumber);
|
||||
}
|
||||
|
||||
fid.setZeroPadding(frameFormats[cell.m_level.getPointer()].first);
|
||||
fid.setStartSeqInd(frameFormats[cell.m_level.getPointer()].second);
|
||||
TFilePath imgPath =
|
||||
checkedLevels[cell.m_level.getPointer()].withFrame(fid);
|
||||
file = imgPath.getQString();
|
||||
}
|
||||
|
||||
TvpJsonLayerLinkItem linkItem;
|
||||
linkItem.build(instance_index, instance_name, file, imgsList[i]);
|
||||
m_link.append(linkItem);
|
||||
}
|
||||
|
||||
if (firstFoundLevel)
|
||||
m_name = QString::fromStdWString(firstFoundLevel->getName());
|
||||
else
|
||||
m_name = QString();
|
||||
|
||||
m_visible = column->isPreviewVisible();
|
||||
m_opacity = (int)column->getOpacity();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void TvpJsonClip::read(const QJsonObject& json) {
|
||||
m_name = json["name"].toString("Untitled");
|
||||
m_cameraSize.setWidth(json["width"].toInt(1920));
|
||||
m_cameraSize.setHeight(json["height"].toInt(1080));
|
||||
m_framerate = json["framerate"].toDouble(24.0);
|
||||
m_pixelaspectratio = json["pixelaspectratio"].toDouble(1.0);
|
||||
m_image_count = json["image-count"].toInt();
|
||||
|
||||
QJsonObject bgObject = json["bg"].toObject();
|
||||
m_bg.setRed(bgObject["red"].toInt(255));
|
||||
m_bg.setGreen(bgObject["green"].toInt(255));
|
||||
m_bg.setBlue(bgObject["blue"].toInt(255));
|
||||
m_bg.setAlpha(255);
|
||||
|
||||
QJsonArray layersArray = json["layers"].toArray();
|
||||
for (int layerIndex = 0; layerIndex < layersArray.size(); layerIndex++) {
|
||||
QJsonObject layerObject = layersArray[layerIndex].toObject();
|
||||
TvpJsonLayer layer;
|
||||
layer.read(layerObject);
|
||||
m_layers.append(layer);
|
||||
}
|
||||
}
|
||||
|
||||
void TvpJsonClip::write(QJsonObject& json) const {
|
||||
json["name"] = m_name;
|
||||
json["width"] = m_cameraSize.width();
|
||||
json["height"] = m_cameraSize.height();
|
||||
json["framerate"] = m_framerate;
|
||||
json["pixelaspectratio"] = m_pixelaspectratio;
|
||||
json["image-count"] = m_image_count;
|
||||
|
||||
QJsonObject bgObject;
|
||||
bgObject["red"] = m_bg.red();
|
||||
bgObject["green"] = m_bg.green();
|
||||
bgObject["blue"] = m_bg.blue();
|
||||
json["bg"] = bgObject;
|
||||
|
||||
QJsonArray layersArray;
|
||||
for (const auto layer : m_layers) {
|
||||
QJsonObject layerObject;
|
||||
layer.write(layerObject);
|
||||
layersArray.append(layerObject);
|
||||
}
|
||||
json["layers"] = layersArray;
|
||||
}
|
||||
|
||||
void TvpJsonClip::build(ToonzScene* scene, TXsheet* xsheet) {
|
||||
TFilePath fp = scene->getScenePath();
|
||||
m_name = QString::fromStdString(fp.getName());
|
||||
|
||||
TStageObjectId cameraId = xsheet->getStageObjectTree()->getCurrentCameraId();
|
||||
TCamera* currentCamera = xsheet->getStageObject(cameraId)->getCamera();
|
||||
TDimension cameraRes = currentCamera->getRes();
|
||||
m_cameraSize = QSize(cameraRes.lx, cameraRes.ly);
|
||||
|
||||
TOutputProperties* outputProperties =
|
||||
scene->getProperties()->getOutputProperties();
|
||||
m_framerate = outputProperties->getFrameRate();
|
||||
|
||||
TPointD cameraDpi = currentCamera->getDpi();
|
||||
m_pixelaspectratio = cameraDpi.y / cameraDpi.x;
|
||||
|
||||
// if the current xsheet is top xsheet in the scene and the output
|
||||
// frame range is specified, set the "to" frame value as duration
|
||||
// TOutputProperties* oprop = scene->getProperties()->getOutputProperties();
|
||||
int from, to, step;
|
||||
if (scene->getTopXsheet() == xsheet &&
|
||||
outputProperties->getRange(from, to, step))
|
||||
m_image_count = to + 1;
|
||||
else
|
||||
m_image_count = xsheet->getFrameCount();
|
||||
|
||||
TPixel bgColor = scene->getProperties()->getBgColor();
|
||||
m_bg = QColor((int)bgColor.r, (int)bgColor.g, (int)bgColor.b);
|
||||
|
||||
if (xsheet->getColumnCount() == 0) return;
|
||||
for (int col = xsheet->getColumnCount() - 1; col >= 0; col--) {
|
||||
if (xsheet->isColumnEmpty(col)) {
|
||||
continue;
|
||||
}
|
||||
TXshCellColumn* column = xsheet->getColumn(col)->getCellColumn();
|
||||
if (!column) {
|
||||
continue;
|
||||
}
|
||||
TvpJsonLayer layer;
|
||||
layer.build(col, scene, column);
|
||||
if (!layer.isEmpty()) m_layers.append(layer);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void TvpJsonProject::read(const QJsonObject& json) {
|
||||
if (json.contains("format")) {
|
||||
QJsonObject formatObject = json["format"].toObject();
|
||||
if (formatObject.contains("extension"))
|
||||
m_formatExtension = formatObject["extension"].toString();
|
||||
}
|
||||
QJsonObject clipObject = json["clip"].toObject();
|
||||
m_clip.read(clipObject);
|
||||
}
|
||||
|
||||
void TvpJsonProject::write(QJsonObject& json) const {
|
||||
QJsonObject formatObject;
|
||||
formatObject["extension"] = m_formatExtension;
|
||||
json["format"] = formatObject;
|
||||
|
||||
QJsonObject clipObject;
|
||||
m_clip.write(clipObject);
|
||||
json["clip"] = clipObject;
|
||||
}
|
||||
|
||||
void TvpJsonProject::build(ToonzScene* scene, TXsheet* xsheet) {
|
||||
TOutputProperties* outputProperties =
|
||||
scene->getProperties()->getOutputProperties();
|
||||
m_formatExtension =
|
||||
QString::fromStdString(outputProperties->getPath().getType());
|
||||
|
||||
m_clip.build(scene, xsheet);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void TvpJsonVersion::read(const QJsonObject& json) {
|
||||
m_major = json["major"].toInt();
|
||||
m_minor = json["minor"].toInt();
|
||||
}
|
||||
|
||||
void TvpJsonVersion::write(QJsonObject& json) const {
|
||||
json["major"] = m_major;
|
||||
json["minor"] = m_minor;
|
||||
}
|
||||
|
||||
void TvpJsonVersion::setVersion(int major, int minor) {
|
||||
m_major = major;
|
||||
m_minor = minor;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void TvpJsonData::read(const QJsonObject& json) {
|
||||
QJsonObject versionObject = json["version"].toObject();
|
||||
m_version.read(versionObject);
|
||||
|
||||
QJsonObject projectObject = json["project"].toObject();
|
||||
m_project.read(projectObject);
|
||||
}
|
||||
|
||||
void TvpJsonData::write(QJsonObject& json) const {
|
||||
QJsonObject versionObject;
|
||||
m_version.write(versionObject);
|
||||
json["version"] = versionObject;
|
||||
|
||||
QJsonObject projectObject;
|
||||
m_project.write(projectObject);
|
||||
json["project"] = projectObject;
|
||||
}
|
||||
|
||||
void TvpJsonData::build(ToonzScene* scene, TXsheet* xsheet) {
|
||||
// currently this i/o is based on version 5.1 files
|
||||
m_version.setVersion(5, 1);
|
||||
m_project.build(scene, xsheet);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class ExportTvpJsonCommand final : public MenuItemHandler {
|
||||
public:
|
||||
ExportTvpJsonCommand() : MenuItemHandler(MI_ExportTvpJson) {}
|
||||
void execute() override;
|
||||
} exportTvpJsonCommand;
|
||||
|
||||
void ExportTvpJsonCommand::execute() {
|
||||
ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
|
||||
if (scene->isUntitled()) {
|
||||
DVGui::error(
|
||||
QObject::tr("TVPaint JSON file cannot be exported from untitled scene. "
|
||||
"Save the scene first."));
|
||||
return;
|
||||
}
|
||||
TXsheet* xsheet = TApp::instance()->getCurrentXsheet()->getXsheet();
|
||||
TFilePath fp = scene->getScenePath().withType("json");
|
||||
|
||||
TvpJsonData tvpJsonData;
|
||||
tvpJsonData.build(scene, xsheet);
|
||||
if (tvpJsonData.isEmpty()) {
|
||||
DVGui::error(
|
||||
QObject::tr("No columns can be exported. Please note the followings:\n "
|
||||
"- The level files must be placed at the same or child "
|
||||
"folder relative to the scene file.\n - Currently only the "
|
||||
"columns containing raster levels can be exported."));
|
||||
return;
|
||||
}
|
||||
|
||||
static GenericSaveFilePopup* savePopup = 0;
|
||||
if (!savePopup) {
|
||||
savePopup =
|
||||
new GenericSaveFilePopup(QObject::tr("Export TVPaint JSON File"));
|
||||
savePopup->addFilterType("json");
|
||||
}
|
||||
if (!scene->isUntitled())
|
||||
savePopup->setFolder(fp.getParentDir());
|
||||
else
|
||||
savePopup->setFolder(
|
||||
TProjectManager::instance()->getCurrentProject()->getScenesPath());
|
||||
savePopup->setFilename(fp.withoutParentDir());
|
||||
fp = savePopup->getPath();
|
||||
if (fp.isEmpty()) return;
|
||||
|
||||
QFile saveFile(fp.getQString());
|
||||
|
||||
if (!saveFile.open(QIODevice::WriteOnly)) {
|
||||
qWarning("Couldn't open save file.");
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject tvpJsonObject;
|
||||
tvpJsonData.write(tvpJsonObject);
|
||||
QJsonDocument saveDoc(tvpJsonObject);
|
||||
QByteArray jsonByteArrayData = saveDoc.toJson();
|
||||
saveFile.write(jsonByteArrayData);
|
||||
|
||||
QString str = QObject::tr("The file %1 has been exported successfully.")
|
||||
.arg(QString::fromStdString(fp.getLevelName()));
|
||||
|
||||
std::vector<QString> buttons = {QObject::tr("OK"),
|
||||
QObject::tr("Open containing folder")};
|
||||
int ret = DVGui::MsgBox(DVGui::INFORMATION, str, buttons);
|
||||
|
||||
if (ret == 2) {
|
||||
TFilePath folderPath = fp.getParentDir();
|
||||
if (TSystem::isUNC(folderPath))
|
||||
QDesktopServices::openUrl(QUrl(folderPath.getQString()));
|
||||
else
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(folderPath.getQString()));
|
||||
}
|
||||
}
|
182
toonz/sources/toonz/tvpjson_io.h
Normal file
182
toonz/sources/toonz/tvpjson_io.h
Normal file
|
@ -0,0 +1,182 @@
|
|||
#pragma once
|
||||
#ifndef TVPJSON_IO_H
|
||||
#define TVPJSON_IO_H
|
||||
|
||||
#include "tfilepath.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QSize>
|
||||
#include <QColor>
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
#include <QStringList>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
class ToonzScene;
|
||||
class TFilePath;
|
||||
class TXsheet;
|
||||
class TXshCellColumn;
|
||||
class QJsonObject;
|
||||
|
||||
namespace TvpJsonIo {
|
||||
|
||||
// Currently only supports "Color" and "Darken" brend modes
|
||||
enum BlendMode {
|
||||
BlendMode_Color,
|
||||
// BlendMode_Behind,
|
||||
// BlendMode_Erase,
|
||||
// BlendMode_Shade,
|
||||
// BlendMode_Light,
|
||||
// BlendMode_Colorize,
|
||||
// BlendMode_Hue,
|
||||
// BlendMode_Saturation,
|
||||
// BlendMode_Value,
|
||||
// BlendMode_Add,
|
||||
// BlendMode_Sub,
|
||||
// BlendMode_Multiply,
|
||||
// BlendMode_Screen,
|
||||
// BlendMode_Replace,
|
||||
// BlendMode_Copy,
|
||||
// BlendMode_Difference,
|
||||
// BlendMode_Divide,
|
||||
// BlendMode_Overlay,
|
||||
// BlendMode_Overlay2,
|
||||
// BlendMode_Light2,
|
||||
// BlendMode_Shade2,
|
||||
// BlendMode_HardLight,
|
||||
// BlendMode_SoftLight,
|
||||
// BlendMode_GrainExtract,
|
||||
// BlendMode_GrainMerge,
|
||||
// BlendMode_Sub2,
|
||||
BlendMode_Darken,
|
||||
// BlendMode_Lighten,
|
||||
BlendModeCount
|
||||
};
|
||||
|
||||
// "description": "information of each cell and instance",
|
||||
class TvpJsonLayerLinkItem {
|
||||
// instance-index "description": "first placed frame of instance (the first
|
||||
// frame is 0)"
|
||||
int m_instance_index;
|
||||
// instance-name "description": "drawing number (starting from 1. 0 is used
|
||||
// for empty frame)"
|
||||
QString m_instance_name;
|
||||
// "description": "instance file path, relative to the scene file"
|
||||
QString m_file;
|
||||
// "description": "each start frame of the image (the first frame is 0)"
|
||||
QList<int> m_images;
|
||||
|
||||
public:
|
||||
void read(const QJsonObject& json);
|
||||
void write(QJsonObject& json) const;
|
||||
void build(int instance_index, QString instance_name, QString file,
|
||||
QList<int> images);
|
||||
};
|
||||
|
||||
class TvpJsonLayer {
|
||||
// "description": "layer name (= level name)"
|
||||
QString m_name;
|
||||
// "description": "visibility"
|
||||
bool m_visible;
|
||||
// "description": "layer index (stacking order), starting from 0"
|
||||
int m_position;
|
||||
// "description": "layer opacity 0-255"
|
||||
int m_opacity;
|
||||
// "description": "start frame of the layer"
|
||||
int m_start;
|
||||
// "description": "end frame of the layer"
|
||||
int m_end;
|
||||
|
||||
// pre_behavior, post_behavior : Unknown. Put 0 for now.
|
||||
|
||||
// "description": "composite mode of the layers. "Color"=Normal,
|
||||
// "Darken"=Darken"
|
||||
BlendMode m_blending_mode;
|
||||
|
||||
// group/red,green,blue "description": "Unknown. Put 0 for now"
|
||||
// QColor m_group
|
||||
|
||||
// "description": "information of each cell and instance",
|
||||
QList<TvpJsonLayerLinkItem> m_link;
|
||||
|
||||
public:
|
||||
void read(const QJsonObject& json);
|
||||
void write(QJsonObject& json) const;
|
||||
void build(int index, ToonzScene*, TXshCellColumn* column);
|
||||
bool isEmpty() { return m_link.isEmpty(); }
|
||||
};
|
||||
|
||||
class TvpJsonClip {
|
||||
// "description": "scene name"
|
||||
QString m_name;
|
||||
// "width": "description": "camera width in pixels"
|
||||
// "height": "description": "camera height in pixels"
|
||||
QSize m_cameraSize;
|
||||
// "description": "frame rate"
|
||||
double m_framerate;
|
||||
// "description": "pixel aspect ratio"
|
||||
double m_pixelaspectratio;
|
||||
// "image-count": "description": "scene frame length"
|
||||
int m_image_count;
|
||||
// bg/red,green,blue "description": "camera BG color(0-255 for each
|
||||
// channel)"
|
||||
QColor m_bg;
|
||||
|
||||
// camera : camera information. Ignore it for now.
|
||||
|
||||
// markin, markout : Ignore for now.
|
||||
|
||||
//"description": "layer information"
|
||||
QList<TvpJsonLayer> m_layers;
|
||||
|
||||
// repeat : Ignore for now.
|
||||
|
||||
public:
|
||||
void read(const QJsonObject& json);
|
||||
void write(QJsonObject& json) const;
|
||||
bool isEmpty() { return m_layers.isEmpty(); }
|
||||
void build(ToonzScene*, TXsheet*);
|
||||
};
|
||||
|
||||
class TvpJsonProject {
|
||||
// format/extension "description": "Output format extension??"
|
||||
QString m_formatExtension;
|
||||
// "description": "scene information"
|
||||
TvpJsonClip m_clip;
|
||||
|
||||
public:
|
||||
void read(const QJsonObject& json);
|
||||
void write(QJsonObject& json) const;
|
||||
void build(ToonzScene*, TXsheet*);
|
||||
bool isEmpty() { return m_clip.isEmpty(); }
|
||||
};
|
||||
|
||||
class TvpJsonVersion {
|
||||
// "description": "major version"
|
||||
int m_major;
|
||||
// "description": "minor version"
|
||||
int m_minor;
|
||||
|
||||
public:
|
||||
void read(const QJsonObject& json);
|
||||
void write(QJsonObject& json) const;
|
||||
void setVersion(int, int);
|
||||
};
|
||||
|
||||
class TvpJsonData {
|
||||
// "description": "version number of this tvp-json format",
|
||||
TvpJsonVersion m_version;
|
||||
// "description": "project information"
|
||||
TvpJsonProject m_project;
|
||||
|
||||
public:
|
||||
void read(const QJsonObject& json);
|
||||
void write(QJsonObject& json) const;
|
||||
void build(ToonzScene*, TXsheet*);
|
||||
bool isEmpty() { return m_project.isEmpty(); }
|
||||
};
|
||||
|
||||
} // namespace TvpJsonIo
|
||||
|
||||
#endif
|
|
@ -1241,6 +1241,12 @@ void FileIconRenderer::run() {
|
|||
QSize(iconSize.lx, iconSize.ly), Qt::KeepAspectRatio));
|
||||
setIcon(rasterFromQPixmap(script));
|
||||
return;
|
||||
} else if (type == "json") {
|
||||
QPixmap json(svgToPixmap(getIconThemePath("mimetypes/60/json_icon.svg"),
|
||||
QSize(iconSize.lx, iconSize.ly),
|
||||
Qt::KeepAspectRatio));
|
||||
setIcon(rasterFromQPixmap(json));
|
||||
return;
|
||||
}
|
||||
|
||||
else {
|
||||
|
|
Loading…
Reference in a new issue