diff --git a/toonz/sources/image/CMakeLists.txt b/toonz/sources/image/CMakeLists.txt index 87953ce..720af0e 100644 --- a/toonz/sources/image/CMakeLists.txt +++ b/toonz/sources/image/CMakeLists.txt @@ -26,6 +26,7 @@ set(HEADERS mesh/tiio_mesh.h exr/tinyexr_otmod.h exr/tiio_exr.h + tzm/tiio_tzm.h ) set(SOURCES @@ -56,6 +57,7 @@ set(SOURCES sprite/tiio_sprite.cpp mesh/tiio_mesh.cpp exr/tiio_exr.cpp + tzm/tiio_tzm.cpp ) diff --git a/toonz/sources/image/tiio.cpp b/toonz/sources/image/tiio.cpp index 97d766e..e0ccc01 100644 --- a/toonz/sources/image/tiio.cpp +++ b/toonz/sources/image/tiio.cpp @@ -76,6 +76,7 @@ #include "./avi/tiio_avi.h" #include "./pli/tiio_pli.h" #include "./tzl/tiio_tzl.h" +#include "./tzm/tiio_tzm.h" #include "./svg/tiio_svg.h" #include "./ffmpeg/tiio_gif.h" #include "./ffmpeg/tiio_webm.h" @@ -141,6 +142,9 @@ void initImageIo(bool lightVersion) { TLevelReader::define("mesh", TLevelReaderMesh::create); TFileType::declare("mesh", TFileType::MESH_IMAGE); + TLevelWriter::define("tzm", tzm::createWriter, false); + TLevelReader::define("tzm", tzm::createReader); + TFileType::declare("tzm", TFileType::META_LEVEL); } // !lightversion TFileType::declare("tpl", TFileType::PALETTE_LEVEL); diff --git a/toonz/sources/image/tzm/tiio_tzm.cpp b/toonz/sources/image/tzm/tiio_tzm.cpp new file mode 100644 index 0000000..7a8421f --- /dev/null +++ b/toonz/sources/image/tzm/tiio_tzm.cpp @@ -0,0 +1,191 @@ + +#include + +#include +#include +#include + +#include +#include + +#include "tiio_tzm.h" + + + +//=========================================================================== + +namespace { + class TLevelWriterTzm; + + class TImageWriterTzm final : public TImageWriter { + private: + TLevelWriterTzm &m_writer; + TFrameId m_frameId; + public: + TImageWriterTzm(const TFilePath &path, TLevelWriterTzm &writer, const TFrameId &frameId): + TImageWriter(path), m_writer(writer), m_frameId(frameId) { } + void save(const TImageP &image) override; + }; + + class TLevelWriterTzm final : public TLevelWriter { + private: + TVariant m_data; + Tofstream *m_stream; + + public: + TLevelWriterTzm(const TFilePath &path, TPropertyGroup *winfo): + TLevelWriter(path, winfo) + { + // lock file for whole time of saving process to avoid collisions + try { + m_stream = new Tofstream(path); + } catch (const std::exception &e) { + throw TImageException(path, e.what()); + } catch (const TException &e) { + throw TImageException(path, to_string(e.getMessage())); + } + + m_data["type"].setString("tzm"); + m_data["version"].setDouble( 0.0 ); + } + + TImageWriterP getFrameWriter(TFrameId frameId) override + { return TImageWriterP(new TImageWriterTzm(m_path, *this, frameId)); } + + void saveFrame(const TFrameId &frameId, const TImageP &image) { + if (const TMetaImage *metaImage = dynamic_cast(image.getPointer())) { + TMetaImage::Reader reader(*metaImage); + TVariant &frameData = m_data["frames"][frameId.expand()]; + TVariant &objectsData = frameData["objects"]; + objectsData.setType(TVariant::List); + for(TMetaObjectRefList::const_iterator i = reader->begin(); i != reader->end(); ++i) { + if (*i) { + TVariant &objectData = objectsData[ objectsData.size() ]; + objectData["type"].setString( (*i)->getTypeName() ); + objectData["data"] = (*i)->data(); + } + } + } + } + + ~TLevelWriterTzm() { + try { + m_data["creator"].setString( m_creator.toStdString() ); + m_data.toStream(*m_stream, true); + delete m_stream; + } catch (const std::exception &e) { + throw TImageException(m_path, e.what()); + } catch (const TException &e) { + throw TImageException(m_path, to_string(e.getMessage())); + } + } + }; + + void TImageWriterTzm::save(const TImageP &image) + { m_writer.saveFrame(m_frameId, image); } +} // end of anonymous namespace for TLevelWriterTzm + +//=========================================================================== + +namespace { + class TLevelReaderTzm; + + class TImageReaderTzm final : public TImageReader { + private: + TLevelReaderTzm &m_reader; + TFrameId m_frameId; + public: + TImageReaderTzm(const TFilePath &path, TLevelReaderTzm &reader, const TFrameId &frameId): + TImageReader(path), m_reader(reader), m_frameId(frameId) { } + TImageP load() override; + }; + + + class TLevelReaderTzm final : public TLevelReader { + private: + TVariant m_data; + TLevelP m_level; + + void warning(const std::string &msg) + { std::cerr << to_string(m_path.getWideString()) << ": " << msg << std::endl; } + + public: + TLevelReaderTzm(const TFilePath &path): + TLevelReader(path) { } + + TImageReaderP getFrameReader(TFrameId frameId) override + { return TImageReaderP(new TImageReaderTzm(m_path, *this, frameId)); } + + TLevelP loadInfo() override { + try { + Tifstream stream(m_path); + m_data.fromStream(stream); + } catch (const std::exception &e) { + throw TImageException(m_path, e.what()); + } catch (const TException &e) { + throw TImageException(m_path, to_string(e.getMessage())); + } + + if (m_data["type"].getString() != "tzm") + warning("seems it's not TZM"); + if (m_data["version"].getDouble() > 0.0 + TConsts::epsilon) + warning( "version (" + + std::to_string(m_data["version"].getDouble()) + + ") is higher than supported (0.0)"); + + const TVariantMap &map = m_data["frames"].getMap(); + for(TVariantMap::const_iterator i = map.begin(); i != map.end(); ++i) { + TFrameId frameId(i->first.str()); + if (frameId.getNumber() < 0) + warning("wrong frame number: " + i->first.str()); + else + if (m_level->getTable()->count(frameId)) + warning(frameId.expand()); + else + m_level->setFrame(frameId, TImageP()); + } + + return m_level; + } + + QString getCreator() override + { return QString::fromStdString( m_data["creator"].getString() ); } + + TImageP loadFrame(const TFrameId &frameId) { + const TVariantMap &map = m_data["frames"].getMap(); + for(TVariantMap::const_iterator i = map.begin(); i != map.end(); ++i) { + if (TFrameId(i->first.str()) == frameId) { + TMetaImage *image = new TMetaImage(); + TMetaImage::Writer writer(*image); + const TVariant &objectsData = i->second["objects"]; + if (objectsData.getType() == TVariant::List) { + for(int j = 0; j < objectsData.size(); ++j) { + const TVariant &objectData = objectsData[j]; + if (!objectData["type"].getString().empty()) { + TMetaObjectR obj( new TMetaObject(objectData["type"].getString()) ); + obj->data() = objectData["data"]; + writer->push_back(obj); + } + } + } + return image; + } + } + return TImageP(); + } + }; + + TImageP TImageReaderTzm::load() + { return m_reader.loadFrame(m_frameId); } +} // end of anonymous namespace for TLevelReaderTzm + +//=========================================================================== + +namespace tzm { + TLevelWriter* createWriter(const TFilePath &path, TPropertyGroup *winfo) + { return new TLevelWriterTzm(path, winfo); } + TLevelReader* createReader(const TFilePath &path) + { return new TLevelReaderTzm(path); } +} + +//============================================================================= diff --git a/toonz/sources/image/tzm/tiio_tzm.h b/toonz/sources/image/tzm/tiio_tzm.h new file mode 100644 index 0000000..07f9a61 --- /dev/null +++ b/toonz/sources/image/tzm/tiio_tzm.h @@ -0,0 +1,14 @@ +#pragma once + +#ifndef TTIO_TZM_INCLUDED +#define TTIO_TZM_INCLUDED + +#include + + +namespace tzm { + TLevelWriter* createWriter(const TFilePath &path, TPropertyGroup *winfo); + TLevelReader* createReader(const TFilePath &path); +} + +#endif // TTIO_TZM_INCLUDED diff --git a/toonz/sources/include/tfiletype.h b/toonz/sources/include/tfiletype.h index cc3d928..05cf3b2 100644 --- a/toonz/sources/include/tfiletype.h +++ b/toonz/sources/include/tfiletype.h @@ -41,6 +41,9 @@ enum Type { AUDIO_LEVEL = 0x20 | LEVEL, PALETTE_LEVEL = 0x40 | LEVEL, + META_IMAGE = 0x80, + META_LEVEL = META_IMAGE | LEVEL, + TABSCENE = 0x2000 | SCENE, TOONZSCENE = 0x4000 | SCENE, diff --git a/toonz/sources/toonzlib/imagebuilders.cpp b/toonz/sources/toonzlib/imagebuilders.cpp index dfb2f4e..a794ecd 100644 --- a/toonz/sources/toonzlib/imagebuilders.cpp +++ b/toonz/sources/toonzlib/imagebuilders.cpp @@ -98,7 +98,7 @@ TImageP ImageLoader::build(int imFlags, void *extData) { lr->doReadPalette(false); if ((m_path.getType() == "pli") || (m_path.getType() == "svg") || - (m_path.getType() == "psd")) + (m_path.getType() == "psd") || (m_path.getType() == "tzm")) lr->loadInfo(); bool isTlvIcon = data->m_icon && m_path.getType() == "tlv"; diff --git a/toonz/sources/toonzlib/toonzscene.cpp b/toonz/sources/toonzlib/toonzscene.cpp index 01c5aa4..ae91a6c 100644 --- a/toonz/sources/toonzlib/toonzscene.cpp +++ b/toonz/sources/toonzlib/toonzscene.cpp @@ -1017,6 +1017,12 @@ static LevelType getLevelType(const TFilePath &fp) { case TFileType::MESH_LEVEL: ret.m_ltype = MESH_XSHLEVEL; break; + + case TFileType::META_IMAGE: + case TFileType::META_LEVEL: + ret.m_ltype = META_XSHLEVEL; + break; + default: break; } @@ -1401,6 +1407,9 @@ TFilePath ToonzScene::getDefaultLevelPath(int levelType, case TZP_XSHLEVEL: levelPath = TFilePath(levelName).withType("tlv"); break; + case META_XSHLEVEL: + levelPath = TFilePath(levelName).withType("tzm"); + break; default: levelPath = TFilePath(levelName + L"..png"); } diff --git a/toonz/sources/toonzlib/txshsimplelevel.cpp b/toonz/sources/toonzlib/txshsimplelevel.cpp index 6c52010..4e9acd5 100644 --- a/toonz/sources/toonzlib/txshsimplelevel.cpp +++ b/toonz/sources/toonzlib/txshsimplelevel.cpp @@ -1019,6 +1019,8 @@ void TXshSimpleLevel::loadData(TIStream &is) { type = TZI_XSHLEVEL; else if (ext == "mesh") type = MESH_XSHLEVEL; + else if (ext == "tzm") + type = META_XSHLEVEL; else type = OVL_XSHLEVEL; } diff --git a/toonz/sources/toonzqt/infoviewer.cpp b/toonz/sources/toonzqt/infoviewer.cpp index b26b850..4f86c24 100644 --- a/toonz/sources/toonzqt/infoviewer.cpp +++ b/toonz/sources/toonzqt/infoviewer.cpp @@ -282,6 +282,8 @@ QString InfoViewerImp::getTypeString() { return "Audio File"; else if (ext == "mesh") return "Toonz Mesh Level"; + else if (ext == "tzm") + return "Toonz Meta Level"; else if (ext == "pic") return "Pic File"; else if (Tiio::makeReader(ext.toStdString()))