diff --git a/stuff/profiles/layouts/rooms/Default/menubar_template.xml b/stuff/profiles/layouts/rooms/Default/menubar_template.xml
index b671f81..09043ae 100644
--- a/stuff/profiles/layouts/rooms/Default/menubar_template.xml
+++ b/stuff/profiles/layouts/rooms/Default/menubar_template.xml
@@ -29,6 +29,7 @@
MI_SoundTrack
MI_ExportXDTS
MI_StopMotionExportImageSequence
+ MI_ExportTvpJson
MI_PrintXsheet
diff --git a/toonz/sources/toonz/CMakeLists.txt b/toonz/sources/toonz/CMakeLists.txt
index fd87193..6332e91 100644
--- a/toonz/sources/toonz/CMakeLists.txt
+++ b/toonz/sources/toonz/CMakeLists.txt
@@ -193,6 +193,7 @@ set(HEADERS
metnum.h
processor.h
predict3d.h
+ tvpjson_io.h
)
set(SOURCES
@@ -361,6 +362,7 @@ set(SOURCES
xdtsimportpopup.cpp
expressionreferencemanager.cpp
tooloptionsshortcutinvoker.cpp
+ tvpjson_io.cpp
# Tracker file
dummyprocessor.cpp
metnum.cpp
diff --git a/toonz/sources/toonz/icons/dark/mimetypes/60/json_icon.svg b/toonz/sources/toonz/icons/dark/mimetypes/60/json_icon.svg
new file mode 100644
index 0000000..e9d83f3
--- /dev/null
+++ b/toonz/sources/toonz/icons/dark/mimetypes/60/json_icon.svg
@@ -0,0 +1,110 @@
+
+
\ No newline at end of file
diff --git a/toonz/sources/toonz/mainwindow.cpp b/toonz/sources/toonz/mainwindow.cpp
index 00e4e1d..7db34e3 100644
--- a/toonz/sources/toonz/mainwindow.cpp
+++ b/toonz/sources/toonz/mainwindow.cpp
@@ -1671,6 +1671,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");
createMenuFileAction("MI_OpenScriptConsole",
diff --git a/toonz/sources/toonz/menubar.cpp b/toonz/sources/toonz/menubar.cpp
index ab47897..33fe8e6 100644
--- a/toonz/sources/toonz/menubar.cpp
+++ b/toonz/sources/toonz/menubar.cpp
@@ -1108,6 +1108,7 @@ QMenuBar *StackedMenuBar::createFullMenuBar() {
#if defined(x64)
addMenuItem(exportMenu, MI_StopMotionExportImageSequence);
#endif
+ addMenuItem(exportMenu, MI_ExportTvpJson);
}
fileMenu->addSeparator();
addMenuItem(fileMenu, MI_PrintXsheet);
diff --git a/toonz/sources/toonz/menubarcommandids.h b/toonz/sources/toonz/menubarcommandids.h
index 05ed07c..eb0f404 100644
--- a/toonz/sources/toonz/menubarcommandids.h
+++ b/toonz/sources/toonz/menubarcommandids.h
@@ -453,4 +453,5 @@
#define MI_FlipPrevGuideStroke "MI_FlipPrevGuideStroke"
#define MI_ExportXDTS "MI_ExportXDTS"
+#define MI_ExportTvpJson "MI_ExportTvpJson"
#endif
diff --git a/toonz/sources/toonz/toonz.qrc b/toonz/sources/toonz/toonz.qrc
index 492add9..69514d3 100644
--- a/toonz/sources/toonz/toonz.qrc
+++ b/toonz/sources/toonz/toonz.qrc
@@ -166,7 +166,8 @@
icons/dark/mimetypes/60/svg_icon.svg
icons/dark/mimetypes/60/audio_icon.svg
icons/dark/mimetypes/60/script_icon.svg
- icons/dark/mimetypes/60/broken_icon.svg
+ icons/dark/mimetypes/60/broken_icon.svg
+ icons/dark/mimetypes/60/json_icon.svg
icons/dark/actions/16/render.svg
diff --git a/toonz/sources/toonz/tvpjson_io.cpp b/toonz/sources/toonz/tvpjson_io.cpp
new file mode 100644
index 0000000..4aaa470
--- /dev/null
+++ b/toonz/sources/toonz/tvpjson_io.cpp
@@ -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
+#include
+#include
+#include
+#include
+
+using namespace TvpJsonIo;
+
+namespace {
+
+QMap 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 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�ɋL���\���ǂ����`�F�b�N�ς݂̃��x��
+ QMap checkedLevels;
+ // register the firstly-found level
+ TXshLevel* firstFoundLevel = nullptr;
+
+ QList cellList;
+ QList> imgsList;
+ QMap> 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 fids;
+ level->getFids(fids);
+ frameFormats[level] = QPair(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());
+ }
+ 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()) { // ��R�}�̏ꍇ
+ 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 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()));
+ }
+}
diff --git a/toonz/sources/toonz/tvpjson_io.h b/toonz/sources/toonz/tvpjson_io.h
new file mode 100644
index 0000000..68bbb11
--- /dev/null
+++ b/toonz/sources/toonz/tvpjson_io.h
@@ -0,0 +1,182 @@
+#pragma once
+#ifndef TVPJSON_IO_H
+#define TVPJSON_IO_H
+
+#include "tfilepath.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+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 m_images;
+
+public:
+ void read(const QJsonObject& json);
+ void write(QJsonObject& json) const;
+ void build(int instance_index, QString instance_name, QString file,
+ QList 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 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�i0-255 for each
+ // channel�j"
+ QColor m_bg;
+
+ // camera : camera information. Ignore it for now.
+
+ // markin, markout : Ignore for now.
+
+ //"description": "layer information"
+ QList 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
\ No newline at end of file
diff --git a/toonz/sources/toonzqt/icongenerator.cpp b/toonz/sources/toonzqt/icongenerator.cpp
index d7df450..d0c4c5e 100644
--- a/toonz/sources/toonzqt/icongenerator.cpp
+++ b/toonz/sources/toonzqt/icongenerator.cpp
@@ -118,7 +118,7 @@ bool getIcon(const std::string &iconName, QPixmap &pix,
ras->getSize().lx > standardSize.lx &&
ras->getSize().ly > standardSize.ly)
isHighDpi = true;
- pix = rasterToQPixmap(ras, false, isHighDpi);
+ pix = rasterToQPixmap(ras, false, isHighDpi);
return true;
}
@@ -187,7 +187,7 @@ void makeChessBackground(const TRaster32P &ras) {
int yCol = (y & 4);
- for (int x = 0; pix != lineEnd; ++x, ++pix)
+ for (int x = 0; pix != lineEnd; ++x, ++pix)
if (pix->m != 255) *pix = overPix((x & 4) == yCol ? gray1 : gray2, *pix);
}
@@ -605,8 +605,8 @@ TRaster32P SplineIconRenderer::generateRaster(
double scaleX = 1, scaleY = 1;
if (sbbox.getLx() > 0.0) scaleX = (double)iconSize.lx / sbbox.getLx();
if (sbbox.getLy() > 0.0) scaleY = (double)iconSize.ly / sbbox.getLy();
- double scale = 0.8 * std::min(scaleX, scaleY);
- TPointD centerStroke = 0.5 * (sbbox.getP00() + sbbox.getP11());
+ double scale = 0.8 * std::min(scaleX, scaleY);
+ TPointD centerStroke = 0.5 * (sbbox.getP00() + sbbox.getP11());
TPointD centerPixmap(iconSize.lx * 0.5, iconSize.ly * 0.5);
glPushMatrix();
tglMultMatrix(TScale(scale).place(centerStroke, centerPixmap));
@@ -976,10 +976,10 @@ TRaster32P IconGenerator::generateVectorFileIcon(const TFilePath &path,
TLevelReaderP lr(path);
TLevelP level = lr->loadInfo();
if (level->begin() == level->end()) return TRaster32P();
- TFrameId frameId = fid;
+ TFrameId frameId = fid;
if (fid == TFrameId::NO_FRAME) frameId = level->begin()->first;
- TImageP img = lr->getFrameReader(frameId)->load();
- TVectorImageP vi = img;
+ TImageP img = lr->getFrameReader(frameId)->load();
+ TVectorImageP vi = img;
if (!vi) return TRaster32P();
vi->setPalette(level->getPalette());
VectorImageIconRenderer vir("", iconSize, vi.getPointer(),
@@ -1117,7 +1117,7 @@ TRaster32P IconGenerator::generateMeshFileIcon(const TFilePath &path,
TLevelP level = lr->loadInfo();
if (level->begin() == level->end()) return TRaster32P();
- TFrameId frameId = fid;
+ TFrameId frameId = fid;
if (fid == TFrameId::NO_FRAME) frameId = level->begin()->first;
TMeshImageP mi = lr->getFrameReader(frameId)->load();
@@ -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 {