From 8b6d1fd6f24f18da82c63d7ef75f60e146c1a4a7 Mon Sep 17 00:00:00 2001 From: shun-iwasawa Date: Mar 12 2021 14:43:30 +0000 Subject: premultiply on loading PNG images --- diff --git a/toonz/sources/image/png/tiio_png.cpp b/toonz/sources/image/png/tiio_png.cpp index dc2808e..fc3d8b6 100644 --- a/toonz/sources/image/png/tiio_png.cpp +++ b/toonz/sources/image/png/tiio_png.cpp @@ -204,7 +204,8 @@ public: png_set_palette_to_rgb(m_png_ptr); // treat the image as RGBA from now on - m_info.m_samplePerPixel = 4; // there are 4 channels per pixel (R, G, B, and A) + m_info.m_samplePerPixel = + 4; // there are 4 channels per pixel (R, G, B, and A) // if there is no alpha channel, then fill it with "255" (full opacity) png_set_filler(m_png_ptr, 0xFF, PNG_FILLER_AFTER); @@ -349,7 +350,8 @@ public: void writeRow(char *buffer) { if (m_color_type == PNG_COLOR_TYPE_RGB_ALPHA || m_color_type == PNG_COLOR_TYPE_GRAY_ALPHA || - m_color_type == PNG_COLOR_TYPE_PALETTE) { // PNG_COLOR_TYPE_PALETTE is expanded to RGBA + m_color_type == PNG_COLOR_TYPE_PALETTE) { // PNG_COLOR_TYPE_PALETTE is + // expanded to RGBA if (m_bit_depth == 16) { TPixel32 *pix = (TPixel32 *)buffer; int i = -2; @@ -377,6 +379,9 @@ public: #else #error "unknown channel order" #endif + + // premultiply here + premult(pix[j]); } } else { TPixel32 *pix = (TPixel32 *)buffer; @@ -388,23 +393,25 @@ public: pix[j].g = m_rowBuffer[i++]; pix[j].b = m_rowBuffer[i++]; #elif defined(TNZ_MACHINE_CHANNEL_ORDER_RGBM) - pix[j].r = m_rowBuffer[i++]; - pix[j].g = m_rowBuffer[i++]; - pix[j].b = m_rowBuffer[i++]; - pix[j].m = m_rowBuffer[i++]; + pix[j].r = m_rowBuffer[i++]; + pix[j].g = m_rowBuffer[i++]; + pix[j].b = m_rowBuffer[i++]; + pix[j].m = m_rowBuffer[i++]; #elif defined(TNZ_MACHINE_CHANNEL_ORDER_BGRM) - pix[j].b = m_rowBuffer[i++]; - pix[j].g = m_rowBuffer[i++]; - pix[j].r = m_rowBuffer[i++]; - pix[j].m = m_rowBuffer[i++]; + pix[j].b = m_rowBuffer[i++]; + pix[j].g = m_rowBuffer[i++]; + pix[j].r = m_rowBuffer[i++]; + pix[j].m = m_rowBuffer[i++]; #elif defined(TNZ_MACHINE_CHANNEL_ORDER_MBGR) - pix[j].m = m_rowBuffer[i++]; - pix[j].b = m_rowBuffer[i++]; - pix[j].g = m_rowBuffer[i++]; - pix[j].r = m_rowBuffer[i++]; + pix[j].m = m_rowBuffer[i++]; + pix[j].b = m_rowBuffer[i++]; + pix[j].g = m_rowBuffer[i++]; + pix[j].r = m_rowBuffer[i++]; #else #error "unknown channel order" #endif + // premultiply here + premult(pix[j]); } } } else // qui gestisce RGB senza alpha. @@ -439,9 +446,9 @@ public: pix[j].b = m_rowBuffer[i++]; #elif defined(TNZ_MACHINE_CHANNEL_ORDER_MBGR) || \ defined(TNZ_MACHINE_CHANNEL_ORDER_BGRM) - pix[j].b = m_rowBuffer[i++]; - pix[j].g = m_rowBuffer[i++]; - pix[j].r = m_rowBuffer[i++]; + pix[j].b = m_rowBuffer[i++]; + pix[j].g = m_rowBuffer[i++]; + pix[j].r = m_rowBuffer[i++]; #else #error "unknown channel order" #endif @@ -454,7 +461,8 @@ public: void writeRow(short *buffer) { if (m_color_type == PNG_COLOR_TYPE_RGB_ALPHA || m_color_type == PNG_COLOR_TYPE_GRAY_ALPHA || - m_color_type == PNG_COLOR_TYPE_PALETTE) { // PNG_COLOR_TYPE_PALETTE is expanded to RGBA + m_color_type == PNG_COLOR_TYPE_PALETTE) { // PNG_COLOR_TYPE_PALETTE is + // expanded to RGBA TPixel64 *pix = (TPixel64 *)buffer; int i = -2; // 0; for (int j = 0; j < m_info.m_lx; j++) { @@ -474,6 +482,9 @@ public: #error "unknown channel order" #endif // pix[j].m = 255; + + // premultiply here + premult(pix[j]); } } else // qui gestisce RGB senza alpha. { // grayscale e' gestito come RGB perche' si usa png_set_gray_to_rgb @@ -849,7 +860,7 @@ void PngWriter::open(FILE *file, const TImageInfo &info) { png_set_PLTE(m_png_ptr, m_info_ptr, palette, m_colormap->size()); } -// png_set_dither(m_png_ptr, palette, 256, 256, 0, 1); + // png_set_dither(m_png_ptr, palette, 256, 256, 0, 1); #if defined(TNZ_MACHINE_CHANNEL_ORDER_MBGR) png_set_bgr(m_png_ptr); @@ -862,7 +873,6 @@ void PngWriter::open(FILE *file, const TImageInfo &info) { #error "unknownchannel order" #endif - png_write_info(m_png_ptr, m_info_ptr); png_set_pHYs(m_png_ptr, m_info_ptr, x_pixels_per_meter, y_pixels_per_meter, 1); @@ -873,6 +883,8 @@ void PngWriter::open(FILE *file, const TImageInfo &info) { bgcolor.index = 0; png_set_tRNS(m_png_ptr, m_info_ptr, alpha, 1, &bgcolor); } + + png_write_info(m_png_ptr, m_info_ptr); } //--------------------------------------------------------- @@ -945,10 +957,10 @@ void PngWriter::writeLine(char *buffer) { tmp[k++] = depremult_pix.g; tmp[k++] = depremult_pix.r; #elif defined(TNZ_MACHINE_CHANNEL_ORDER_BGRM) - tmp[k++] = depremult_pix.b; - tmp[k++] = depremult_pix.g; - tmp[k++] = depremult_pix.r; - tmp[k++] = depremult_pix.m; + tmp[k++] = depremult_pix.b; + tmp[k++] = depremult_pix.g; + tmp[k++] = depremult_pix.r; + tmp[k++] = depremult_pix.m; #else #error "unknown channel order" #endif @@ -961,7 +973,7 @@ void PngWriter::writeLine(char *buffer) { int k = 0; for (int j = 0; j < m_info.m_lx; j++) { -// tmp = (pix->r&0xe0)|((pix->g&0xe0)>>3) | ((pix->b&0xc0)>>6); + // tmp = (pix->r&0xe0)|((pix->g&0xe0)>>3) | ((pix->b&0xc0)>>6); #if defined(TNZ_MACHINE_CHANNEL_ORDER_MRGB) || \ defined(TNZ_MACHINE_CHANNEL_ORDER_RGBM) diff --git a/toonz/sources/include/toonz/toonzscene.h b/toonz/sources/include/toonz/toonzscene.h index 3c0c97b..8bedae4 100644 --- a/toonz/sources/include/toonz/toonzscene.h +++ b/toonz/sources/include/toonz/toonzscene.h @@ -112,9 +112,9 @@ public: /*! \return The \a coded path to be used for import. */ - TFilePath getImportedLevelPath(const TFilePath path) - const; //!< Builds the path to be used during a level import - //!< operation. + TFilePath getImportedLevelPath( + const TFilePath path) const; //!< Builds the path to be used during a + //!< level import operation. /*! \details If convertion is required, a new level file will be created and \p levelPath will be substituted with its new path. @@ -267,9 +267,11 @@ private: TLevelSet *m_levelSet; TProject *m_project; TContentHistory *m_contentHistory; - bool m_isUntitled; //!< Whether the scene is untitled. - //! \sa The setUntitled() member function. - VersionNumber m_versionNumber; + bool m_isUntitled; //!< Whether the scene is untitled. + //! \sa The setUntitled() member function. + VersionNumber m_versionNumber; // last saved scene file version. Note that + // currently it is not match with OT version. + // TODO: Revise VersionNumber with OT version private: // noncopyable diff --git a/toonz/sources/tcomposer/tcomposer.cpp b/toonz/sources/tcomposer/tcomposer.cpp index f425e3c..936d515 100644 --- a/toonz/sources/tcomposer/tcomposer.cpp +++ b/toonz/sources/tcomposer/tcomposer.cpp @@ -30,6 +30,9 @@ #include "toutputproperties.h" #include "toonz/imagestyles.h" #include "tproperty.h" +#include "toonz/levelset.h" +#include "toonz/txshsimplelevel.h" +#include "toonz/levelproperties.h" // TnzSound includes #include "tnzsound.h" @@ -226,6 +229,35 @@ void tcomposerRunOutOfContMemHandler(unsigned long size) { TImageCache::instance()->clear(true); exit(2); } + +// Check if the scene saved with the previous version AND the premultiply option +// is set to PNG level setting +void UnsetPremultiplyOptionsInPngLevels(ToonzScene *scene) { + if (scene->getVersionNumber() < + VersionNumber(71, 1)) { // V1.4 = 71.0 , V1.5 = 71.1 + QStringList modifiedPNGLevelNames; + std::vector levels; + scene->getLevelSet()->listLevels(levels); + for (auto level : levels) { + if (!level || !level->getSimpleLevel()) continue; + TFilePath path = level->getPath(); + if (path.isEmpty() || path.getType() != "png") continue; + if (level->getSimpleLevel()->getProperties()->doPremultiply()) { + level->getSimpleLevel()->getProperties()->setDoPremultiply(false); + modifiedPNGLevelNames.append(QString::fromStdWString(level->getName())); + } + } + if (!modifiedPNGLevelNames.isEmpty()) { + std::string msg = + "The Premultiply options in the following levels are disabled, since " + "PNG files are premultiplied on loading in the current version:" + + modifiedPNGLevelNames.join(", ").toStdString(); + cout << msg << endl; + m_userLog->info(msg); + } + } +} + } // namespace //============================================================================================== @@ -406,7 +438,7 @@ static std::pair generateMovie(ToonzScene *scene, const TFilePath &fp, r0 = r0 - 1; r1 = r1 - 1; - if (r0 < 0) r0 = 0; + if (r0 < 0) r0 = 0; if (r1 < 0 || r1 >= scene->getFrameCount()) r1 = scene->getFrameCount() - 1; string msg; assert(r1 >= r0); @@ -724,7 +756,7 @@ int main(int argc, char *argv[]) { TVectorBrushStyle::setRootDir(libraryFolder); TPalette::setRootDir(libraryFolder); TImageStyle::setLibraryDir(libraryFolder); - TFilePath cacheRoot = ToonzFolder::getCacheRootFolder(); + TFilePath cacheRoot = ToonzFolder::getCacheRootFolder(); if (cacheRoot.isEmpty()) cacheRoot = TEnv::getStuffDir() + "cache"; TImageCache::instance()->setRootDir(cacheRoot); // #endif @@ -819,6 +851,10 @@ int main(int argc, char *argv[]) { // return false; } + // Check if the scene saved with the previous version AND the premultiply + // option is set to PNG level setting + UnsetPremultiplyOptionsInPngLevels(scene); + msg = "scene loaded"; cout << "scene loaded" << endl; m_userLog->info(msg); @@ -991,8 +1027,8 @@ int main(int argc, char *argv[]) { DVGui::info(QString::fromStdString(msg)); TImageCache::instance()->clear(true); } catch (TException &e) { - msg = "Untrapped exception: " + ::to_string(e.getMessage()), cout << msg - << endl; + msg = "Untrapped exception: " + ::to_string(e.getMessage()), + cout << msg << endl; m_userLog->error(msg); TImageCache::instance()->clear(true); } catch (...) { diff --git a/toonz/sources/toonz/iocommand.cpp b/toonz/sources/toonz/iocommand.cpp index 40d72ff..550d9df 100644 --- a/toonz/sources/toonz/iocommand.cpp +++ b/toonz/sources/toonz/iocommand.cpp @@ -670,6 +670,16 @@ void ChildLevelResourceImporter::process(TXshSimpleLevel *sl) { sl->load(); } catch (...) { } + + // Check if the scene saved with the previous version AND the premultiply + // option is set to PNG level setting + if (m_childScene->getVersionNumber() < + VersionNumber(71, 1)) { // V1.4 = 71.0 , V1.5 = 71.1 + if (!path.isEmpty() && path.getType() == "png" && + sl->getProperties()->doPremultiply()) + sl->getProperties()->setDoPremultiply(false); + } + sl->release(); } @@ -1994,6 +2004,31 @@ bool IoCmd::loadScene(const TFilePath &path, bool updateRecentFile, } } + // Check if the scene saved with the previous version AND the premultiply + // option is set to PNG level setting + if (scene->getVersionNumber() < + VersionNumber(71, 1)) { // V1.4 = 71.0 , V1.5 = 71.1 + QStringList modifiedPNGLevelNames; + std::vector levels; + scene->getLevelSet()->listLevels(levels); + for (auto level : levels) { + if (!level || !level->getSimpleLevel()) continue; + TFilePath path = level->getPath(); + if (path.isEmpty() || path.getType() != "png") continue; + if (level->getSimpleLevel()->getProperties()->doPremultiply()) { + level->getSimpleLevel()->getProperties()->setDoPremultiply(false); + modifiedPNGLevelNames.append(QString::fromStdWString(level->getName())); + } + } + if (!modifiedPNGLevelNames.isEmpty()) { + DVGui::info(QObject::tr("The Premultiply options in the following levels " + "are disabled, since PNG files are premultiplied " + "on loading in the current version: %1") + .arg(modifiedPNGLevelNames.join(", "))); + app->getCurrentScene()->setDirtyFlag(true); + } + } + printf("%s:%s loadScene() completed :\n", __FILE__, __FUNCTION__); return true; } diff --git a/toonz/sources/toonzlib/preferences.cpp b/toonz/sources/toonzlib/preferences.cpp index 0852996..3cf1a29 100644 --- a/toonz/sources/toonzlib/preferences.cpp +++ b/toonz/sources/toonzlib/preferences.cpp @@ -109,9 +109,10 @@ void getDefaultLevelFormats(LevelFormatVector &lfv) { lfv[1].m_options.m_premultiply = true; // for all PNG files, set premultiply by default - lfv[2].m_name = Preferences::tr("PNG"); - lfv[2].m_pathFormat = QRegExp("..*\\.png", Qt::CaseInsensitive); - lfv[2].m_options.m_premultiply = true; + // UPDATE : from V1.5, PNG images are premultiplied on loading + // lfv[2].m_name = Preferences::tr("PNG"); + // lfv[2].m_pathFormat = QRegExp("..*\\.png", + // Qt::CaseInsensitive); lfv[2].m_options.m_premultiply = true; } } @@ -193,6 +194,32 @@ void getValue(QSettings &settings, getValue(settings, lfv[lf]); } settings.endArray(); + + // from OT V1.5, PNG images are premultiplied on loading. + // Leaving the premultiply option will cause unwanted double operation. + // So, check the loaded options and modify it "silently". + bool changed = false; + LevelFormatVector::iterator it = lfv.begin(); + while (it != lfv.end()) { + if ((*it).m_name == Preferences::tr("PNG") && + (*it).m_pathFormat == QRegExp("..*\\.png", Qt::CaseInsensitive) && + (*it).m_options.m_premultiply == true) { + LevelOptions defaultValue; + defaultValue.m_premultiply = true; + // if other parameters are the same as deafault, just erase the item + if ((*it).m_options == defaultValue) it = lfv.erase(it); + // if there are some adjustments by user, then disable only premultiply + // option + else { + (*it).m_options.m_premultiply = false; + ++it; + } + changed = true; + } else + ++it; + } + // overwrite the setting + if (changed) _setValue(settings, lfv); } } // namespace diff --git a/toonz/sources/toonzlib/toonzscene.cpp b/toonz/sources/toonzlib/toonzscene.cpp index 3bc0d96..d9ccae2 100644 --- a/toonz/sources/toonzlib/toonzscene.cpp +++ b/toonz/sources/toonzlib/toonzscene.cpp @@ -60,8 +60,11 @@ TOfflineGL *currentOfflineGL = 0; // Utility functions //============================================================================= namespace { - -const VersionNumber l_currentVersion(71, 0); +// tentatively update the scene file version from 71.0 to 71.1 in order to +// manage PNG level settings +const VersionNumber l_currentVersion(71, 1); +// TODO: Revise VersionNumber with OT version (converting 71.0 -> 14.0 , 71.1 +// -> 15.0) //----------------------------------------------------------------------------- @@ -345,12 +348,9 @@ bool ToonzScene::isUntitled() const { //----------------------------------------------------------------------------- void ToonzScene::load(const TFilePath &path, bool withProgressDialog) { - loadNoResources(path); // This loads a version number .. - loadResources(withProgressDialog); // .. this uses the version number .. - - setVersionNumber( - VersionNumber()); // .. but scene instances in memory do not retain -} // a version number beyond resource loading + loadNoResources(path); + loadResources(withProgressDialog); +} //----------------------------------------------------------------------------- @@ -456,7 +456,9 @@ void ToonzScene::loadTnzFile(const TFilePath &fp) { while (is.matchTag(tagName)) { if (tagName == "generator") { std::string program = is.getString(); - reading22 = program.find("2.2") != std::string::npos; + // TODO: This obsolete condition should be removed before releasing OT + // v2.2 ! + reading22 = program.find("2.2") != std::string::npos; } else if (tagName == "properties") m_properties->loadData(is, false); else if (tagName == "palette") // per compatibilita' beta1 @@ -685,6 +687,8 @@ void ToonzScene::save(const TFilePath &fp, TXsheet *subxsh) { } else { if (wasUntitled) deleteUntitledScene(oldScenePath.getParentDir()); } + // update the last saved version + setVersionNumber(l_currentVersion); } //-----------------------------------------------------------------------------