Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// TnzCore includes
Toshihiro Shimizu 890ddd
#include "tsystem.h"
Toshihiro Shimizu 890ddd
#include "trasterimage.h"
Toshihiro Shimizu 890ddd
#include "tiio.h"
Toshihiro Shimizu 890ddd
#include "timageinfo.h"
Toshihiro Shimizu 890ddd
#include "tcontenthistory.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// TnzLib includes
Toshihiro Shimizu 890ddd
#include "toonz/txshleveltypes.h"
Toshihiro Shimizu 890ddd
#include "toonz/imagemanager.h"
Toshihiro Shimizu 890ddd
#include "toonz/toonzscene.h"
Toshihiro Shimizu 890ddd
#include "toonz/levelproperties.h"
Toshihiro Shimizu 890ddd
#include "toonz/preferences.h"
Toshihiro Shimizu 890ddd
#include "toonz/sceneproperties.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "toonz/levelupdater.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//*****************************************************************************************
Toshihiro Shimizu 890ddd
//    Local namespace stuff
Toshihiro Shimizu 890ddd
//*****************************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
inline bool supportsRandomAccess(const TFilePath &fp) {
Shinya Kitaoka 120a6e
  const std::string &type = fp.getType();
Shinya Kitaoka 120a6e
  return type == "tlv" ||  // TLVs do support random access
Shinya Kitaoka 120a6e
         // type == "pli" ||                                       // PLIs... I
Shinya Kitaoka 120a6e
         // thought they would - but no :(
Shinya Kitaoka 120a6e
         // type == "mov" ||                                       // MOVs are
Shinya Kitaoka 120a6e
         // 'on the way' to support it... for now, no
Shinya Kitaoka 120a6e
         fp.getDots() == "..";  // Multi-file levels of course do
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void enforceBpp(TPropertyGroup *pg, int bpp, bool upgradeOnly) {
Shinya Kitaoka 120a6e
  // Most properties have a "Bits Per Pixel" property. Enforce the M there in
Shinya Kitaoka 120a6e
  // case.
Shinya Kitaoka 120a6e
  TEnumProperty *bppProp = (TEnumProperty *)pg->getProperty("Bits Per Pixel");
Shinya Kitaoka 120a6e
  if (bppProp) {
Shinya Kitaoka 120a6e
    typedef TEnumProperty::Range Range;
Shinya Kitaoka 120a6e
    const Range &range = bppProp->getRange();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Retrieve current index
Shinya Kitaoka 120a6e
    int idx = bppProp->getIndex();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Search for a suitable 32-bit or 64-bit value
Shinya Kitaoka 120a6e
    int currentBpp = upgradeOnly ? std::stoi(bppProp->getValueAsString()) : 0;
Shinya Kitaoka 120a6e
    int targetBpp = (std::numeric_limits<int>::max)(), targetIdx = -1;</int>
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    int i, count = (int)range.size();
Shinya Kitaoka 120a6e
    for (i = 0; i < count; ++i) {
Shinya Kitaoka 120a6e
      int bppEntry = std::stoi(range[i]);
Shinya Kitaoka 120a6e
      if ((bppEntry % bpp == 0) && currentBpp <= bppEntry &&
Shinya Kitaoka 120a6e
          bppEntry < targetBpp)
Shinya Kitaoka 120a6e
        targetBpp = bppEntry, targetIdx = i;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (targetIdx >= 0) bppProp->setIndex(targetIdx);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Some properties have an "Alpha Channel" TBoolProperty (PNGs, currently). In
Shinya Kitaoka 120a6e
  // case, check that.
Shinya Kitaoka 120a6e
  if (bpp % 32 == 0) {
Shinya Kitaoka 120a6e
    TBoolProperty *alphaProp =
Shinya Kitaoka 120a6e
        (TBoolProperty *)pg->getProperty("Alpha Channel");
Shinya Kitaoka 120a6e
    if (alphaProp) alphaProp->setValue(true);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//*****************************************************************************************
Toshihiro Shimizu 890ddd
//    LevelUpdater implementation
Toshihiro Shimizu 890ddd
//*****************************************************************************************
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
LevelUpdater::LevelUpdater()
Shinya Kitaoka 120a6e
    : m_pg(0)
Shinya Kitaoka 120a6e
    , m_inputLevel(0)
Shinya Kitaoka 120a6e
    , m_currIdx(0)
Shinya Kitaoka 120a6e
    , m_imageInfo(0)
Shinya Kitaoka 120a6e
    , m_usingTemporaryFile(false)
Shinya Kitaoka 120a6e
    , m_opened(false) {}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
LevelUpdater::LevelUpdater(TXshSimpleLevel *sl)
Shinya Kitaoka 120a6e
    : m_pg(0)
Shinya Kitaoka 120a6e
    , m_inputLevel(0)
Shinya Kitaoka 120a6e
    , m_imageInfo(0)
Shinya Kitaoka 120a6e
    , m_currIdx(0)
Shinya Kitaoka 120a6e
    , m_opened(false)
Shinya Kitaoka 120a6e
    , m_usingTemporaryFile(false) {
Shinya Kitaoka 120a6e
  open(sl);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
LevelUpdater::LevelUpdater(const TFilePath &fp, TPropertyGroup *lwProperties)
Shinya Kitaoka 120a6e
    : m_pg(0)
Shinya Kitaoka 120a6e
    , m_inputLevel(0)
Shinya Kitaoka 120a6e
    , m_imageInfo(0)
Shinya Kitaoka 120a6e
    , m_currIdx(0)
Shinya Kitaoka 120a6e
    , m_opened(false)
Shinya Kitaoka 120a6e
    , m_usingTemporaryFile(false) {
Shinya Kitaoka 120a6e
  open(fp, lwProperties);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
LevelUpdater::~LevelUpdater() {
Shinya Kitaoka 120a6e
  // Please, observe that the try-catch below here is NOT OPTIONAL.
Shinya Kitaoka 120a6e
  // IT IS AN ERROR TO THROW INSIDE A DESTRUCTOR. EVER.
Shinya Kitaoka 120a6e
  // Doing so damages the stack unwinding process - namely, it interferes
Shinya Kitaoka 120a6e
  // with the destruction of OTHER objects going out of scope.
Shinya Kitaoka 120a6e
  // C++ does NOT react well to that (ie could terminate() the process).
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  try {
Shinya Kitaoka 120a6e
    close();
Shinya Kitaoka 120a6e
  } catch (...) {
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void LevelUpdater::reset() {
Shinya Kitaoka 120a6e
  m_lw     = TLevelWriterP();
Shinya Kitaoka 120a6e
  m_lwPath = TFilePath();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  m_lr         = TLevelReaderP();
Shinya Kitaoka 120a6e
  m_inputLevel = TLevelP();
Shinya Kitaoka 120a6e
  m_sl         = TXshSimpleLevelP();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  delete m_pg;
Shinya Kitaoka 120a6e
  m_pg = 0;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (m_imageInfo) {
Shinya Kitaoka 120a6e
    delete m_imageInfo->m_properties;
Shinya Kitaoka 120a6e
    delete m_imageInfo;
Shinya Kitaoka 120a6e
    m_imageInfo = 0;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  m_fids.clear();
Shinya Kitaoka 120a6e
  m_currIdx = 0;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  m_usingTemporaryFile = false;
Shinya Kitaoka 120a6e
  m_opened             = false;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void LevelUpdater::buildSourceInfo(const TFilePath &fp) {
Shinya Kitaoka 120a6e
  try {
Shinya Kitaoka 120a6e
    m_lr = TLevelReaderP(fp);
Shinya Kitaoka 120a6e
    assert(m_lr);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    m_lr->enableRandomAccessRead(
Shinya Kitaoka 120a6e
        true);  // Movie files are intended with a constant fps
Shinya Kitaoka 120a6e
                // should be made the default... TODO!
Shinya Kitaoka 120a6e
    m_inputLevel = m_lr->loadInfo();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    const TImageInfo *info = m_lr->getImageInfo();
Shinya Kitaoka 120a6e
    if (info) {
Shinya Kitaoka 120a6e
      m_imageInfo = new TImageInfo(
Shinya Kitaoka 120a6e
          *info);  // Clone the info. The originals are owned by the reader.
Shinya Kitaoka 120a6e
      if (info->m_properties)
Shinya Kitaoka 120a6e
        m_imageInfo->m_properties =
Shinya Kitaoka 120a6e
            info->m_properties->clone();  // Same for these (unfortunately,
Shinya Kitaoka 120a6e
                                          // TImageInfo is currently
Shinya Kitaoka 120a6e
                                          // no more than a struct...)
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  } catch (...) {
Shinya Kitaoka 120a6e
    // The level exists but could not be read.
Shinya Kitaoka 120a6e
    // Allowing write to a surviving temporary in this case...
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    m_lr         = TLevelReaderP();
Shinya Kitaoka 120a6e
    m_inputLevel = TLevelP(0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (m_imageInfo) {
Shinya Kitaoka 120a6e
      delete m_imageInfo->m_properties;
Shinya Kitaoka 120a6e
      delete m_imageInfo;
Shinya Kitaoka 120a6e
      m_imageInfo = 0;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void LevelUpdater::buildProperties(const TFilePath &fp) {
Shinya Kitaoka 120a6e
  // Ensure that at least the default properties for specified fp.getType()
Shinya Kitaoka 120a6e
  // exist.
Shinya Kitaoka 120a6e
  m_pg = (m_imageInfo && m_imageInfo->m_properties)
Shinya Kitaoka 120a6e
             ? m_imageInfo->m_properties->clone()
Shinya Kitaoka 120a6e
             : Tiio::makeWriterProperties(fp.getType());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (!m_pg) {
Shinya Kitaoka 120a6e
    // If no suitable pg could be found, the extension must be wrong. Reset and
Shinya Kitaoka 120a6e
    // throw.
Shinya Kitaoka 120a6e
    reset();
Shinya Kitaoka 120a6e
    throw TException("Unrecognized file format");
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  assert(m_pg);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void LevelUpdater::open(const TFilePath &fp, TPropertyGroup *pg) {
Shinya Kitaoka 120a6e
  assert(!m_lw);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Find out if a corresponding level already exists on disk - in that case,
Shinya Kitaoka 120a6e
  // load it
Shinya Kitaoka 120a6e
  bool existsLevel = TSystem::doesExistFileOrLevel(fp);
Shinya Kitaoka 120a6e
  if (existsLevel)
Shinya Kitaoka 120a6e
    buildSourceInfo(fp);  // Could be !m_lr if level could not be read
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build Output Properties if needed
Shinya Kitaoka 120a6e
  if (pg)
Shinya Kitaoka 120a6e
    m_pg = pg->clone();
Shinya Kitaoka 120a6e
  else
Shinya Kitaoka 120a6e
    buildProperties(fp);  // Throws only if not even the default properties
Shinya Kitaoka 120a6e
                          // could be found - ie, bad file type
Shinya Kitaoka 120a6e
  try {
Shinya Kitaoka 120a6e
    // Decide whether the update procedure requires a temporary file for
Shinya Kitaoka 120a6e
    // appending
Shinya Kitaoka 120a6e
    m_usingTemporaryFile = existsLevel && !supportsRandomAccess(fp);
Shinya Kitaoka 120a6e
    if (m_usingTemporaryFile) {
Shinya Kitaoka 120a6e
      // The level requires a temporary to write frames to. Upon closing, the
Shinya Kitaoka 120a6e
      // original level
Shinya Kitaoka 120a6e
      // is deleted and the temporary takes its place. Note that m_lw takes
Shinya Kitaoka 120a6e
      // ownership of the properties group.
Shinya Kitaoka 120a6e
      m_lwPath = getNewTemporaryFilePath(fp);
Shinya Kitaoka 120a6e
      m_lw     = TLevelWriterP(m_lwPath, m_pg->clone());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      if (m_inputLevel)
Shinya Kitaoka 120a6e
        for (TLevel::Iterator it = m_inputLevel->begin();
Shinya Kitaoka 120a6e
             it != m_inputLevel->end(); ++it)
Shinya Kitaoka 120a6e
          m_fids.push_back(it->first);
Shinya Kitaoka 120a6e
    } else {
Shinya Kitaoka 120a6e
      m_lr =
Shinya Kitaoka 120a6e
          TLevelReaderP();  // Release the reader. This is necessary since the
Shinya Kitaoka 120a6e
      m_lw = TLevelWriterP(
Shinya Kitaoka 120a6e
          fp, m_pg->clone());  // original file itself will be MODIFIED.
Shinya Kitaoka 120a6e
      m_lwPath = fp;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  } catch (...) {
Shinya Kitaoka 120a6e
    // In this case, TLevelWriterP(..) failed, that object was never contructed,
Shinya Kitaoka 120a6e
    // the assignment m_lw never took place. And m_lw == 0.
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Reset state and rethrow
Shinya Kitaoka 120a6e
    reset();
Shinya Kitaoka 120a6e
    throw;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // In case the writer saves icons inside the output level (TLV case), set the
Shinya Kitaoka 120a6e
  // associated icon size now
Shinya Kitaoka 120a6e
  TDimension iconSize = Preferences::instance()->getIconSize();
Shinya Kitaoka 120a6e
  assert(iconSize.lx > 0 && iconSize.ly > 0);
Shinya Kitaoka 120a6e
  m_lw->setIconSize(iconSize);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_opened = true;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void LevelUpdater::open(TXshSimpleLevel *sl) {
Shinya Kitaoka 120a6e
  assert(!m_lw);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  assert(sl && sl->getScene());
Shinya Kitaoka 120a6e
  m_sl = sl;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  const TFilePath &fp = sl->getScene()->decodeFilePath(sl->getPath());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Find out if a corresponding level already exists on disk - in that case,
Shinya Kitaoka 120a6e
  // load it
Shinya Kitaoka 120a6e
  bool existsLevel = TSystem::doesExistFileOrLevel(fp);
Shinya Kitaoka 120a6e
  if (existsLevel)
Shinya Kitaoka 120a6e
    buildSourceInfo(fp);  // Could be !m_lr if level could not be read
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build Output Properties
Shinya Kitaoka 120a6e
  buildProperties(fp);  // May throw if not even the default properties could be
Shinya Kitaoka 120a6e
                        // retrieved
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // If there was no level on disk, or the level properties require the alpha
Shinya Kitaoka 120a6e
  // channel, enforce the
Shinya Kitaoka 120a6e
  // bpp accordingly on m_pg.
Shinya Kitaoka 120a6e
  LevelProperties *levelProperties = sl->getProperties();
Shinya Kitaoka 120a6e
  assert(levelProperties);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (levelProperties->hasAlpha() || !existsLevel) {
Shinya Kitaoka 120a6e
    int bpp = levelProperties->hasAlpha()
Shinya Kitaoka 120a6e
                  ? std::min(32, levelProperties->getBpp())
Shinya Kitaoka 120a6e
                  : levelProperties->getBpp();
Shinya Kitaoka 120a6e
    enforceBpp(m_pg, bpp, existsLevel);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Should sl->getPalette() be enforced on m_lw too? It was not present in the
Shinya Kitaoka 120a6e
  // old code...
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  try {
Shinya Kitaoka 120a6e
    // Decide whether the update procedure requires a temporary file for
Shinya Kitaoka 120a6e
    // appending
Shinya Kitaoka 120a6e
    m_usingTemporaryFile = existsLevel && !supportsRandomAccess(fp);
Shinya Kitaoka 120a6e
    if (m_usingTemporaryFile) {
Shinya Kitaoka 120a6e
      // The level requires a temporary to write frames to. Upon closing, the
Shinya Kitaoka 120a6e
      // original level
Shinya Kitaoka 120a6e
      // is deleted and the temporary takes its place.
Shinya Kitaoka 120a6e
      m_lwPath = getNewTemporaryFilePath(fp);
Shinya Kitaoka 120a6e
      m_lw     = TLevelWriterP(m_lwPath, m_pg->clone());
Shinya Kitaoka 120a6e
    } else {
Shinya Kitaoka 120a6e
      m_lr = TLevelReaderP();                   // Release the reader
Shinya Kitaoka 120a6e
      m_lw = TLevelWriterP(fp, m_pg->clone());  // Open for write the usual way
Shinya Kitaoka 120a6e
      m_lwPath = fp;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  } catch (...) {
Shinya Kitaoka 120a6e
    // Reset state and rethrow
Shinya Kitaoka 120a6e
    reset();
Shinya Kitaoka 120a6e
    throw;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Load the frames directly from sl
Shinya Kitaoka 120a6e
  sl->getFids(m_fids);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // In case the writer saves icons inside the output level (TLV case), set the
Shinya Kitaoka 120a6e
  // associated icon size now
Shinya Kitaoka 120a6e
  TDimension iconSize = Preferences::instance()->getIconSize();
Shinya Kitaoka 120a6e
  assert(iconSize.lx > 0 && iconSize.ly > 0);
Shinya Kitaoka 120a6e
  m_lw->setIconSize(iconSize);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (sl->getContentHistory())
Shinya Kitaoka 120a6e
    m_lw->setContentHistory(m_sl->getContentHistory()->clone());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_opened = true;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
TFilePath LevelUpdater::getNewTemporaryFilePath(const TFilePath &fp) {
Shinya Kitaoka 120a6e
  TFilePath fp2;
Shinya Kitaoka 120a6e
  int count = 1;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  for (;;) {
shun-iwasawa f8c030
    // changed the temporary name as the previous naming (like
shun-iwasawa f8c030
    // "filename__1.png") had been misteken as sequential images
shun-iwasawa f8c030
    fp2 = fp.withName(fp.getWideName() + L"_ottmp" + std::to_wstring(count++));
Shinya Kitaoka 120a6e
    if (!TSystem::doesExistFileOrLevel(fp2)) break;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  return fp2;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void LevelUpdater::addFramesTo(int endIdx) {
Shinya Kitaoka 120a6e
  if (m_sl) {
Shinya Kitaoka 120a6e
    // The simple level case can be optimized since some level's images could
Shinya Kitaoka 120a6e
    // already be present
Shinya Kitaoka 120a6e
    // in memory. Images are accessed through the level itself.
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    for (; m_currIdx < endIdx; ++m_currIdx) {
Shinya Kitaoka 120a6e
      TImageP img = m_sl->getFullsampledFrame(m_fids[m_currIdx],
Shinya Kitaoka 120a6e
                                              ImageManager::dontPutInCache);
Shinya Kitaoka 120a6e
      assert(img);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      if (!img && m_lr) {
Shinya Kitaoka 120a6e
        // This should actually never happen. ImageManager should already ensure
Shinya Kitaoka 120a6e
        // that img exists.
Shinya Kitaoka 120a6e
        // However, as last resort let's just look at the file too...
Shinya Kitaoka 120a6e
        img = m_lr->getFrameReader(m_fids[m_currIdx])->load();
Shinya Kitaoka 120a6e
        if (img) img->setPalette(m_sl->getPalette());
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      if (img) m_lw->getFrameWriter(m_fids[m_currIdx])->save(img);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  } else if (m_lr) {
Shinya Kitaoka 120a6e
    // Otherwise, just look in the file directly
Shinya Kitaoka 120a6e
    for (; m_currIdx < endIdx; ++m_currIdx) {
Shinya Kitaoka 120a6e
      TImageP img = m_lr->getFrameReader(m_fids[m_currIdx])->load();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      if (img) m_lw->getFrameWriter(m_fids[m_currIdx])->save(img);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void LevelUpdater::update(const TFrameId &fid, const TImageP &img) {
Shinya Kitaoka 120a6e
  // Resume open for write
Shinya Kitaoka 120a6e
  resume();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (!m_usingTemporaryFile) {
Shinya Kitaoka 120a6e
    // Plain random access write if supported
Shinya Kitaoka 120a6e
    m_lw->getFrameWriter(fid)->save(img);
Shinya Kitaoka 120a6e
    return;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Otherwise, we must add every frame preceding fid, and *then* add img.
Shinya Kitaoka 120a6e
  // NOTE: This requires that the image sequence is already sorted by fid.
Shinya Kitaoka 120a6e
  addFramesTo(std::lower_bound(m_fids.begin() + m_currIdx, m_fids.end(), fid) -
Shinya Kitaoka 120a6e
              m_fids.begin());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Save the passed image. In case it overwrites a frame, erase that from the
Shinya Kitaoka 120a6e
  // list too.
Shinya Kitaoka 120a6e
  m_lw->getFrameWriter(fid)->save(img);
Shinya Kitaoka 120a6e
  if (m_currIdx < int(m_fids.size()) && m_fids[m_currIdx] == fid) ++m_currIdx;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void LevelUpdater::close() {
Shinya Kitaoka 120a6e
  if (!m_opened) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Resume open for write
Shinya Kitaoka 120a6e
  resume();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  try {
Shinya Kitaoka 120a6e
    if (m_usingTemporaryFile) {
Shinya Kitaoka 120a6e
      // Add all remaining frames still in m_fids
Shinya Kitaoka 120a6e
      addFramesTo((int)m_fids.size());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Currently written level is temporary. It must be renamed to its
Shinya Kitaoka 120a6e
      // originally intended path,
Shinya Kitaoka 120a6e
      // if it's possible to write there. Now, if it's writable, in particular
Shinya Kitaoka 120a6e
      // it should be readable,
Shinya Kitaoka 120a6e
      // so m_lr should exist.
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // If not... well, the file was corrupt or something. Instead than
Shinya Kitaoka 120a6e
      // attempting to delete it,
Shinya Kitaoka 120a6e
      // we're begin conservative - this means that no data is lost, but
Shinya Kitaoka 120a6e
      // unfortunately temporaries
Shinya Kitaoka 120a6e
      // might pile up...
Shinya Kitaoka 120a6e
      if (m_lr) {
Shinya Kitaoka 120a6e
        TFilePath finalPath(m_lr->getFilePath()), tempPath(m_lw->getFilePath());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        // Release m_lr and m_lw - to be sure that no file is kept open while
Shinya Kitaoka 120a6e
        // renaming.
Shinya Kitaoka 120a6e
        // NOTE: releasing m_lr and m_lw should not throw anything. As stated
Shinya Kitaoka 120a6e
        // before, throwing
Shinya Kitaoka 120a6e
        //       in destructors is bad. I'm not sure this is actually guaranteed
Shinya Kitaoka 120a6e
        //       in Toonz, however :(
Shinya Kitaoka 120a6e
        m_lr = TLevelReaderP(), m_lw = TLevelWriterP();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        // Rename the level
Shinya Kitaoka 120a6e
        TSystem::removeFileOrLevel_throw(finalPath);
Shinya Kitaoka 120a6e
        TSystem::renameFileOrLevel_throw(finalPath,
Shinya Kitaoka 120a6e
                                         tempPath);  // finalPath <- tempPath
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        // If present, add known trailing files
Shinya Kitaoka 120a6e
        if (finalPath.getType() == "tlv") {
Shinya Kitaoka 120a6e
          // Palette file
Shinya Kitaoka 120a6e
          TFilePath finalPalette = finalPath.withType("tpl");
Shinya Kitaoka 120a6e
          TFilePath tempPalette  = tempPath.withType("tpl");
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          if (TFileStatus(finalPalette).doesExist()) {
Shinya Kitaoka 120a6e
            if (TFileStatus(tempPalette).doesExist())
Shinya Kitaoka 120a6e
              TSystem::deleteFile(finalPalette);
Shinya Kitaoka 120a6e
            TSystem::renameFile(finalPalette, tempPalette);
Shinya Kitaoka 120a6e
          }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          // History file
Shinya Kitaoka 120a6e
          TFilePath finalHistory = finalPath.withType("hst");
Shinya Kitaoka 120a6e
          TFilePath tempHistory  = tempPath.withType("hst");
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          if (TFileStatus(tempHistory).doesExist()) {
Shinya Kitaoka 120a6e
            if (TFileStatus(finalHistory).doesExist())
Shinya Kitaoka 120a6e
              TSystem::deleteFile(finalHistory);
Shinya Kitaoka 120a6e
            TSystem::renameFile(finalHistory, tempHistory);
Shinya Kitaoka 120a6e
          }
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // NOTE: If for some reason m_lr was not present and we were using a
Shinya Kitaoka 120a6e
      // temporary file, no
Shinya Kitaoka 120a6e
      // renaming takes place. Users could see the __x temporaries and,
Shinya Kitaoka 120a6e
      // eventually, rename them manually
Shinya Kitaoka 120a6e
      // or see what's wrong with the unwritable file.
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Reset the updater's status
Shinya Kitaoka 120a6e
    reset();
Shinya Kitaoka 120a6e
  } catch (...) {
Shinya Kitaoka 120a6e
    // Some temporary object could not be renamed. Or some remaining frame could
Shinya Kitaoka 120a6e
    // not be added.
Shinya Kitaoka 120a6e
    // Hopefully, it was not about closing m_lr or m_lw.
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // However, we still intend to reset the updater's status before rethrowing.
Shinya Kitaoka 120a6e
    reset();
Shinya Kitaoka 120a6e
    throw;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void LevelUpdater::flush() {
Shinya Kitaoka 120a6e
  assert(m_opened);
Shinya Kitaoka 120a6e
  if (!m_lw) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // In case the level writer could not be destroyed (bad, should really not
Shinya Kitaoka 120a6e
  // throw btw),
Shinya Kitaoka 120a6e
  // reset and rethrow
Shinya Kitaoka 120a6e
  try {
Shinya Kitaoka 120a6e
    m_lw = TLevelWriterP();
Shinya Kitaoka 120a6e
  } catch (...) {
Shinya Kitaoka 120a6e
    reset();
Shinya Kitaoka 120a6e
    throw;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void LevelUpdater::resume() {
Shinya Kitaoka 120a6e
  assert(m_opened);
Shinya Kitaoka 120a6e
  if (m_lw) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  try {
Shinya Kitaoka 120a6e
    m_lw = TLevelWriterP(m_lwPath, m_pg->clone());
Shinya Kitaoka 120a6e
  } catch (...) {
Shinya Kitaoka 120a6e
    reset();
Shinya Kitaoka 120a6e
    throw;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}