From a55f60e466a3a1838c145d877ee73e1f000da13a Mon Sep 17 00:00:00 2001 From: shun-iwasawa Date: Thu, 7 Oct 2021 11:47:55 +0900 Subject: [PATCH] 30 bit display feature --- toonz/sources/common/trop/quickput.cpp | 453 ++++++++++++++++++ toonz/sources/include/tgl.h | 6 + toonz/sources/include/toonz/preferences.h | 1 + .../include/toonz/preferencesitemids.h | 1 + toonz/sources/include/tpixelutils.h | 52 +- toonz/sources/toonz/flipbook.cpp | 71 +-- toonz/sources/toonz/imageviewer.cpp | 21 +- toonz/sources/toonz/main.cpp | 10 + toonz/sources/toonz/preferencespopup.cpp | 101 ++++ toonz/sources/toonz/preferencespopup.h | 37 ++ toonz/sources/toonz/previewfxmanager.cpp | 4 +- toonz/sources/toonz/sceneviewer.cpp | 21 +- toonz/sources/toonzlib/imagepainter.cpp | 41 +- toonz/sources/toonzlib/preferences.cpp | 1 + 14 files changed, 747 insertions(+), 73 deletions(-) diff --git a/toonz/sources/common/trop/quickput.cpp b/toonz/sources/common/trop/quickput.cpp index 253c334f..a9c9ae1f 100644 --- a/toonz/sources/common/trop/quickput.cpp +++ b/toonz/sources/common/trop/quickput.cpp @@ -942,6 +942,454 @@ void doQuickPutNoFilter(const TRaster32P &dn, const TRasterGR8P &up, up->unlock(); } +//============================================================================= +//============================================================================= + +void doQuickPutNoFilter(const TRaster64P &dn, const TRaster64P &up, + const TAffine &aff, bool doPremultiply, + bool firstColumn) { + // se aff := TAffine(sx, 0, tx, 0, sy, ty) e' degenere la controimmagine + // di up e' un segmento (o un punto) + if ((aff.a11 * aff.a22 - aff.a12 * aff.a21) == 0) return; + + // contatore bit di shift + const int PADN = 16; + + // max dimensioni di up gestibili (limite imposto dal numero di bit + // disponibili per la parte intera di xL, yL) + assert(std::max(up->getLx(), up->getLy()) < + (1 << (8 * sizeof(int) - PADN - 1))); + + TRectD boundingBoxD = + TRectD(convert(dn->getBounds())) * + (aff * TRectD(-0.5, -0.5, up->getLx() - 0.5, up->getLy() - 0.5)); + + // clipping + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + // clipping y su dn + int yMin = std::max(tfloor(boundingBoxD.y0), 0); + + // clipping y su dn + int yMax = std::min(tceil(boundingBoxD.y1), dn->getLy() - 1); + + // clipping x su dn + int xMin = std::max(tfloor(boundingBoxD.x0), 0); + + // clipping x su dn + int xMax = std::min(tceil(boundingBoxD.x1), dn->getLx() - 1); + + // inversa di aff + TAffine invAff = inv(aff); + + // nel disegnare la y-esima scanline di dn, il passaggio al pixel + // successivo comporta l'incremento (deltaXD, deltaYD) delle coordinate del + // pixel corrispondente di up + double deltaXD = invAff.a11; + double deltaYD = invAff.a21; + + // deltaXD "TLonghizzato" (round) + int deltaXL = tround(deltaXD * (1 << PADN)); + + // deltaYD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); + + // se aff "TLonghizzata" (round) e' degenere la controimmagine di up e' un + // segmento (o un punto) + if ((deltaXL == 0) && (deltaYL == 0)) return; + + // TINT32 predecessore di up->getLx() + int lxPred = up->getLx() * (1 << PADN) - 1; + + // TINT32 predecessore di up->getLy() + int lyPred = up->getLy() * (1 << PADN) - 1; + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + + TPixel64 *dnRow = dn->pixels(yMin); + TPixel64 *upBasePix = up->pixels(); + + // scorre le scanline di boundingBoxD + for (int y = yMin; y <= yMax; y++, dnRow += dnWrap) { + // (1) equazione k-parametrica della y-esima scanline di boundingBoxD: + // (xMin, y) + k*(1, 0), k = 0, ..., (xMax - xMin) + + // (2) equazione k-parametrica dell'immagine mediante invAff di (1): + // invAff*(xMin, y) + k*(deltaXD, deltaYD), + // k = kMin, ..., kMax con 0 <= kMin <= kMax <= (xMax - xMin) + + // calcola kMin, kMax per la scanline corrente + // intersecando la (2) con i lati di up + + // il segmento [a, b] di up e' la controimmagine mediante aff della + // porzione di scanline [ (xMin, y), (xMax, y) ] di dn + + // TPointD b = invAff*TPointD(xMax, y); + TPointD a = invAff * TPointD(xMin, y); + + // (xL0, yL0) sono le coordinate di a (inizializzate per il round) + // in versione "TLonghizzata" + // 0 <= xL0 + k*deltaXL + // < up->getLx()*(1<getLy()*(1<getLx()*(1< + // 0 <= xL0 + k*deltaXL + // <= lxPred + // + // 0 <= yL0 + k*deltaYL + // < up->getLy()*(1< + // 0 <= yL0 + k*deltaYL + // <= lyPred + + // calcola kMinX, kMaxX + if (deltaXL == 0) { + // [a, b] verticale esterno ad up+(bordo destro/basso) + if ((xL0 < 0) || (lxPred < xL0)) continue; + // altrimenti usa solo + // kMinY, kMaxY ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaXL > 0) { + // [a, b] esterno ad up+(bordo destro/basso) + if (lxPred < xL0) continue; + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else // (deltaXL < 0) + { + // [a, b] esterno ad up+(bordo destro/basso) + if (xL0 < 0) continue; + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + + // calcola kMinY, kMaxY + if (deltaYL == 0) { + // [a, b] orizzontale esterno ad up+(bordo destro/basso) + if ((yL0 < 0) || (lyPred < yL0)) continue; + // altrimenti usa solo + // kMinX, kMaxX ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaYL > 0) { + // [a, b] esterno ad up+(bordo destro/basso) + if (lyPred < yL0) continue; + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + // [a, b] esterno ad up+(bordo destro/basso) + if (yL0 < 0) continue; + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + + // calcola kMin, kMax effettuando anche il clippind su dn + int kMin = std::max({kMinX, kMinY, (int)0}); + int kMax = std::min({kMaxX, kMaxY, xMax - xMin}); + + TPixel64 *dnPix = dnRow + xMin + kMin; + TPixel64 *dnEndPix = dnRow + xMin + kMax + 1; + + // (xL, yL) sono le coordinate (inizializzate per il round) + // in versione "TLonghizzata" del pixel corrente di up + int xL = xL0 + (kMin - 1) * deltaXL; // inizializza xL + int yL = yL0 + (kMin - 1) * deltaYL; // inizializza yL + + // scorre i pixel sulla y-esima scanline di boundingBoxD + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + yL += deltaYL; + + // il punto di up TPointD(xL/(1<> PADN; // round + int yI = yL >> PADN; // round + + assert((0 <= xI) && (xI <= up->getLx() - 1) && (0 <= yI) && + (yI <= up->getLy() - 1)); + + TPixel64 upPix = *(upBasePix + (yI * upWrap + xI)); + + if (firstColumn) upPix.m = 65535; + if (upPix.m == 0) continue; + + if (upPix.m == 65535) + *dnPix = upPix; + else if (doPremultiply) + *dnPix = quickOverPixPremult(*dnPix, upPix); + else + *dnPix = quickOverPix(*dnPix, upPix); + } + } + dn->unlock(); + up->unlock(); +} +//============================================================================= + +void doQuickPutNoFilter(const TRaster64P &dn, const TRaster32P &up, + const TAffine &aff, bool doPremultiply, + bool firstColumn) { + // se aff := TAffine(sx, 0, tx, 0, sy, ty) e' degenere la controimmagine + // di up e' un segmento (o un punto) + if ((aff.a11 * aff.a22 - aff.a12 * aff.a21) == 0) return; + + // contatore bit di shift + const int PADN = 16; + + // max dimensioni di up gestibili (limite imposto dal numero di bit + // disponibili per la parte intera di xL, yL) + assert(std::max(up->getLx(), up->getLy()) < + (1 << (8 * sizeof(int) - PADN - 1))); + + TRectD boundingBoxD = + TRectD(convert(dn->getBounds())) * + (aff * TRectD(-0.5, -0.5, up->getLx() - 0.5, up->getLy() - 0.5)); + + // clipping + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + // clipping y su dn + int yMin = std::max(tfloor(boundingBoxD.y0), 0); + + // clipping y su dn + int yMax = std::min(tceil(boundingBoxD.y1), dn->getLy() - 1); + + // clipping x su dn + int xMin = std::max(tfloor(boundingBoxD.x0), 0); + + // clipping x su dn + int xMax = std::min(tceil(boundingBoxD.x1), dn->getLx() - 1); + + // inversa di aff + TAffine invAff = inv(aff); + + // nel disegnare la y-esima scanline di dn, il passaggio al pixel + // successivo comporta l'incremento (deltaXD, deltaYD) delle coordinate del + // pixel corrispondente di up + double deltaXD = invAff.a11; + double deltaYD = invAff.a21; + + // deltaXD "TLonghizzato" (round) + int deltaXL = tround(deltaXD * (1 << PADN)); + + // deltaYD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); + + // se aff "TLonghizzata" (round) e' degenere la controimmagine di up e' un + // segmento (o un punto) + if ((deltaXL == 0) && (deltaYL == 0)) return; + + // TINT32 predecessore di up->getLx() + int lxPred = up->getLx() * (1 << PADN) - 1; + + // TINT32 predecessore di up->getLy() + int lyPred = up->getLy() * (1 << PADN) - 1; + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + + TPixel64 *dnRow = dn->pixels(yMin); + TPixel32 *upBasePix = up->pixels(); + + // scorre le scanline di boundingBoxD + for (int y = yMin; y <= yMax; y++, dnRow += dnWrap) { + // (1) equazione k-parametrica della y-esima scanline di boundingBoxD: + // (xMin, y) + k*(1, 0), k = 0, ..., (xMax - xMin) + + // (2) equazione k-parametrica dell'immagine mediante invAff di (1): + // invAff*(xMin, y) + k*(deltaXD, deltaYD), + // k = kMin, ..., kMax con 0 <= kMin <= kMax <= (xMax - xMin) + + // calcola kMin, kMax per la scanline corrente + // intersecando la (2) con i lati di up + + // il segmento [a, b] di up e' la controimmagine mediante aff della + // porzione di scanline [ (xMin, y), (xMax, y) ] di dn + + // TPointD b = invAff*TPointD(xMax, y); + TPointD a = invAff * TPointD(xMin, y); + + // (xL0, yL0) sono le coordinate di a (inizializzate per il round) + // in versione "TLonghizzata" + // 0 <= xL0 + k*deltaXL + // < up->getLx()*(1<getLy()*(1<getLx()*(1< + // 0 <= xL0 + k*deltaXL + // <= lxPred + // + // 0 <= yL0 + k*deltaYL + // < up->getLy()*(1< + // 0 <= yL0 + k*deltaYL + // <= lyPred + + // calcola kMinX, kMaxX + if (deltaXL == 0) { + // [a, b] verticale esterno ad up+(bordo destro/basso) + if ((xL0 < 0) || (lxPred < xL0)) continue; + // altrimenti usa solo + // kMinY, kMaxY ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaXL > 0) { + // [a, b] esterno ad up+(bordo destro/basso) + if (lxPred < xL0) continue; + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else // (deltaXL < 0) + { + // [a, b] esterno ad up+(bordo destro/basso) + if (xL0 < 0) continue; + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + + // calcola kMinY, kMaxY + if (deltaYL == 0) { + // [a, b] orizzontale esterno ad up+(bordo destro/basso) + if ((yL0 < 0) || (lyPred < yL0)) continue; + // altrimenti usa solo + // kMinX, kMaxX ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaYL > 0) { + // [a, b] esterno ad up+(bordo destro/basso) + if (lyPred < yL0) continue; + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + // [a, b] esterno ad up+(bordo destro/basso) + if (yL0 < 0) continue; + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + + // calcola kMin, kMax effettuando anche il clippind su dn + int kMin = std::max({kMinX, kMinY, (int)0}); + int kMax = std::min({kMaxX, kMaxY, xMax - xMin}); + + TPixel64 *dnPix = dnRow + xMin + kMin; + TPixel64 *dnEndPix = dnRow + xMin + kMax + 1; + + // (xL, yL) sono le coordinate (inizializzate per il round) + // in versione "TLonghizzata" del pixel corrente di up + int xL = xL0 + (kMin - 1) * deltaXL; // inizializza xL + int yL = yL0 + (kMin - 1) * deltaYL; // inizializza yL + + // scorre i pixel sulla y-esima scanline di boundingBoxD + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + yL += deltaYL; + + // il punto di up TPointD(xL/(1<> PADN; // round + int yI = yL >> PADN; // round + + assert((0 <= xI) && (xI <= up->getLx() - 1) && (0 <= yI) && + (yI <= up->getLy() - 1)); + + TPixel32 upPix = *(upBasePix + (yI * upWrap + xI)); + + if (firstColumn) upPix.m = 255; + if (upPix.m == 0) continue; + + TPixel64 upPix64 = toPixel64(upPix); + if (upPix.m == 255) + *dnPix = upPix64; + else if (doPremultiply) + *dnPix = quickOverPixPremult(*dnPix, upPix64); + else + *dnPix = quickOverPix(*dnPix, upPix64); + } + } + dn->unlock(); + up->unlock(); +} //============================================================================= void doQuickPutFilter(const TRaster32P &dn, const TRaster32P &up, double sx, @@ -4268,6 +4716,7 @@ void quickPut(const TRasterP &dn, const TRasterP &up, const TAffine &aff, TRaster32P up32 = up; TRasterGR8P dn8 = dn; TRasterGR8P up8 = up; + TRaster64P dn64 = dn; TRaster64P up64 = up; if (up8 && dn32) { @@ -4294,6 +4743,10 @@ void quickPut(const TRasterP &dn, const TRasterP &up, const TAffine &aff, } } else if (dn32 && up64) doQuickPutNoFilter(dn32, up64, aff, doPremultiply, firstColumn); + else if (dn64 && up64) + doQuickPutNoFilter(dn64, up64, aff, doPremultiply, firstColumn); + else if (dn64 && up32) + doQuickPutNoFilter(dn64, up32, aff, doPremultiply, firstColumn); else throw TRopException("raster type mismatch"); } diff --git a/toonz/sources/include/tgl.h b/toonz/sources/include/tgl.h index 538b8f61..b13c754e 100644 --- a/toonz/sources/include/tgl.h +++ b/toonz/sources/include/tgl.h @@ -68,6 +68,12 @@ class TCubic; #define TGL_TYPE GL_UNSIGNED_BYTE #endif +//============================================================================= +// settings for 30bit display + +#define TGL_TexFmt10 GL_RGB10_A2 +#define TGL_TYPE16 GL_UNSIGNED_SHORT + //============================================================================= #ifdef _DEBUG diff --git a/toonz/sources/include/toonz/preferences.h b/toonz/sources/include/toonz/preferences.h index a941347f..12abaf7c 100644 --- a/toonz/sources/include/toonz/preferences.h +++ b/toonz/sources/include/toonz/preferences.h @@ -234,6 +234,7 @@ public: } void setColorCalibrationLutPath(QString monitorName, QString path); QString getColorCalibrationLutPath(QString &monitorName) const; + bool is30bitDisplayEnabled() const { return getBoolValue(displayIn30bit); } bool isViewerIndicatorEnabled() const { return getBoolValue(viewerIndicatorEnabled); diff --git a/toonz/sources/include/toonz/preferencesitemids.h b/toonz/sources/include/toonz/preferencesitemids.h index b7f111cc..78f9f9e2 100644 --- a/toonz/sources/include/toonz/preferencesitemids.h +++ b/toonz/sources/include/toonz/preferencesitemids.h @@ -45,6 +45,7 @@ enum PreferencesItemId { colorCalibrationEnabled, colorCalibrationLutPaths, showIconsInMenu, + displayIn30bit, viewerIndicatorEnabled, //---------- diff --git a/toonz/sources/include/tpixelutils.h b/toonz/sources/include/tpixelutils.h index 04c2c2ac..6ffd0be1 100644 --- a/toonz/sources/include/tpixelutils.h +++ b/toonz/sources/include/tpixelutils.h @@ -236,6 +236,12 @@ inline TPixel32 quickOverPixPremult(const TPixel32 &bot, const TPixel32 &top) { //----------------------------------------------------------------------------- +inline TPixel64 quickOverPixPremult(const TPixel64 &bot, const TPixel64 &top) { + return quickOverPixPremultT(bot, top); +} + +//----------------------------------------------------------------------------- + inline TPixel64 quickOverPix(const TPixel64 &bot, const TPixel64 &top) { return quickOverPixT(bot, top); } @@ -294,7 +300,7 @@ inline TPixel32 overPixOnBlack(const TPixel32 &top) { //----------------------------------------------------------------------------- /*! this function combines two GR8 pixels returning the darker. -*/ + */ inline TPixelGR8 over(const TPixelGR8 &bot, const TPixelGR8 &top) { return TPixelGR8(std::min(bot.value, top.value)); @@ -528,26 +534,30 @@ void sub(T &pixout, const T &pixin, double v) { template void mult(T &pixout, const T &pixin, double v) { double r, g, b, m; - r = pixin.r + v; - g = pixin.g + v; - b = pixin.b + v; - m = pixin.m + v; - pixout.r = - (r < 0) ? 0 : ((r < T::maxChannelValue) - ? troundp(r * (pixout.r / (double)T::maxChannelValue)) - : pixout.r); - pixout.g = - (g < 0) ? 0 : ((g < T::maxChannelValue) - ? troundp(g * (pixout.g / (double)T::maxChannelValue)) - : pixout.g); - pixout.b = - (b < 0) ? 0 : ((b < T::maxChannelValue) - ? troundp(b * (pixout.b / (double)T::maxChannelValue)) - : pixout.b); - pixout.m = - (m < 0) ? 0 : ((m < T::maxChannelValue) - ? troundp(m * (pixout.m / (double)T::maxChannelValue)) - : pixout.m); + r = pixin.r + v; + g = pixin.g + v; + b = pixin.b + v; + m = pixin.m + v; + pixout.r = (r < 0) + ? 0 + : ((r < T::maxChannelValue) + ? troundp(r * (pixout.r / (double)T::maxChannelValue)) + : pixout.r); + pixout.g = (g < 0) + ? 0 + : ((g < T::maxChannelValue) + ? troundp(g * (pixout.g / (double)T::maxChannelValue)) + : pixout.g); + pixout.b = (b < 0) + ? 0 + : ((b < T::maxChannelValue) + ? troundp(b * (pixout.b / (double)T::maxChannelValue)) + : pixout.b); + pixout.m = (m < 0) + ? 0 + : ((m < T::maxChannelValue) + ? troundp(m * (pixout.m / (double)T::maxChannelValue)) + : pixout.m); } //--------------------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/flipbook.cpp b/toonz/sources/toonz/flipbook.cpp index 0d0bbba7..80d96307 100644 --- a/toonz/sources/toonz/flipbook.cpp +++ b/toonz/sources/toonz/flipbook.cpp @@ -163,34 +163,33 @@ inline TRectD getImageBoundsD(const TImageP &img) { FlipBook::FlipBook(QWidget *parent, QString viewerTitle, std::vector flipConsoleButtonMask, UCHAR flags, bool isColorModel) //, bool showOnlyPlayBackgroundButton) - : QWidget(parent), - m_viewerTitle(viewerTitle), - m_levelNames(), - m_levels(), - m_playSound(false), - m_snd(0), - m_player(0) - //, m_doCompare(false) - , - m_currentFrameToSave(0), - m_lw(), - m_lr(), - m_loadPopup(0), - m_savePopup(0), - m_shrink(1), - m_isPreviewFx(false), - m_previewedFx(0), - m_previewXsh(0), - m_previewUpdateTimer(this), - m_xl(0), - m_title1(), - m_poolIndex(-1), - m_freezed(false), - m_loadbox(), - m_dim(), - m_loadboxes(), - m_freezeButton(0), - m_flags(flags) { + : QWidget(parent) + , m_viewerTitle(viewerTitle) + , m_levelNames() + , m_levels() + , m_playSound(false) + , m_snd(0) + , m_player(0) + //, m_doCompare(false) + , m_currentFrameToSave(0) + , m_lw() + , m_lr() + , m_loadPopup(0) + , m_savePopup(0) + , m_shrink(1) + , m_isPreviewFx(false) + , m_previewedFx(0) + , m_previewXsh(0) + , m_previewUpdateTimer(this) + , m_xl(0) + , m_title1() + , m_poolIndex(-1) + , m_freezed(false) + , m_loadbox() + , m_dim() + , m_loadboxes() + , m_freezeButton(0) + , m_flags(flags) { setAcceptDrops(true); setFocusPolicy(Qt::StrongFocus); @@ -1103,7 +1102,7 @@ void FlipBook::setLevel(const TFilePath &fp, TPalette *palette, int from, fromIndex = level->begin()->first.getNumber(); toIndex = (--level->end())->first.getNumber(); if (m_imageViewer->isColorModel()) - current = m_flipConsole->getCurrentFrame(); + current = m_flipConsole->getCurrentFrame(); incrementalIndexing = true; } else { TLevel::Iterator it = level->begin(); @@ -1141,10 +1140,9 @@ void FlipBook::setLevel(const TFilePath &fp, TPalette *palette, int from, levelToPush.m_incrementalIndexing = incrementalIndexing; int formatIdx = Preferences::instance()->matchLevelFormat(fp); - if (formatIdx >= 0 && - Preferences::instance() - ->levelFormat(formatIdx) - .m_options.m_premultiply) { + if (formatIdx >= 0 && Preferences::instance() + ->levelFormat(formatIdx) + .m_options.m_premultiply) { levelToPush.m_premultiply = true; } @@ -1595,6 +1593,9 @@ TImageP FlipBook::getCurrentImage(int frame) { lx = m_loadbox.getLx(); } + if (Preferences::instance()->is30bitDisplayEnabled()) + ir->enable16BitRead(true); + TImageP img = ir->load(); if (img) { @@ -2146,7 +2147,7 @@ void FlipBook::minimize(bool doMinimize) { */ void FlipBook::loadAndCacheAllTlvImages(Level level, int fromFrame, int toFrame) { - TFilePath fp = level.m_fp; + TFilePath fp = level.m_fp; if (!m_lr || (fp != m_lr->getFilePath())) m_lr = TLevelReaderP(fp); if (!m_lr) return; @@ -2223,7 +2224,7 @@ FlipBook *viewFile(const TFilePath &path, int from, int to, int step, if (step == -1 || shrink == -1) { int _step = 1, _shrink = 1; Preferences::instance()->getViewValues(_shrink, _step); - if (step == -1) step = _step; + if (step == -1) step = _step; if (shrink == -1) shrink = _shrink; } diff --git a/toonz/sources/toonz/imageviewer.cpp b/toonz/sources/toonz/imageviewer.cpp index fe605b4b..9052975c 100644 --- a/toonz/sources/toonz/imageviewer.cpp +++ b/toonz/sources/toonz/imageviewer.cpp @@ -244,6 +244,11 @@ ImageViewer::ImageViewer(QWidget *parent, FlipBook *flipbook, if (Preferences::instance()->isColorCalibrationEnabled()) m_lutCalibrator = new LutCalibrator(); + +#if QT_VERSION >= 0x051000 + if (Preferences::instance()->is30bitDisplayEnabled()) + setTextureFormat(TGL_TexFmt10); +#endif } //----------------------------------------------------------------------------- @@ -488,13 +493,19 @@ void ImageViewer::resizeGL(int w, int h) { // remake fbo with new size if (m_lutCalibrator && m_lutCalibrator->isValid()) { if (m_fbo) delete m_fbo; - m_fbo = new QOpenGLFramebufferObject(w, h); + if (Preferences::instance()->is30bitDisplayEnabled()) { + QOpenGLFramebufferObjectFormat format; + format.setInternalTextureFormat(TGL_TexFmt10); + m_fbo = new QOpenGLFramebufferObject(w, h, format); + } else // normally, initialize with GL_RGBA8 format + m_fbo = new QOpenGLFramebufferObject(w, h); } } //----------------------------------------------------------------------------- void ImageViewer::paintGL() { + initializeOpenGLFunctions(); if (m_lutCalibrator && m_lutCalibrator->isValid()) m_fbo->bind(); TDimension viewerSize(width(), height()); @@ -1422,8 +1433,14 @@ void ImageViewer::onPreferenceChanged(const QString& prefName) { m_lutCalibrator->initialize(); connect(context(), SIGNAL(aboutToBeDestroyed()), this, SLOT(onContextAboutToBeDestroyed())); - if (m_lutCalibrator->isValid() && !m_fbo) + if (m_lutCalibrator->isValid() && !m_fbo) { + if (Preferences::instance()->is30bitDisplayEnabled()) { + QOpenGLFramebufferObjectFormat format; + format.setInternalTextureFormat(TGL_TexFmt10); + m_fbo = new QOpenGLFramebufferObject(width(), height(), format); + } else // normally, initialize with GL_RGBA8 format m_fbo = new QOpenGLFramebufferObject(width(), height()); + } doneCurrent(); } update(); diff --git a/toonz/sources/toonz/main.cpp b/toonz/sources/toonz/main.cpp index e8bfddcc..f5f0da19 100644 --- a/toonz/sources/toonz/main.cpp +++ b/toonz/sources/toonz/main.cpp @@ -528,6 +528,16 @@ int main(int argc, char *argv[]) { crInstall(&pInfo); #endif + // prepare for 30bit display + if (Preferences::instance()->is30bitDisplayEnabled()) { + QSurfaceFormat sFmt = QSurfaceFormat::defaultFormat(); + sFmt.setRedBufferSize(10); + sFmt.setGreenBufferSize(10); + sFmt.setBlueBufferSize(10); + sFmt.setAlphaBufferSize(2); + QSurfaceFormat::setDefaultFormat(sFmt); + } + // Initialize thread components TThread::init(); diff --git a/toonz/sources/toonz/preferencespopup.cpp b/toonz/sources/toonz/preferencespopup.cpp index aa320aeb..9eb8394b 100644 --- a/toonz/sources/toonz/preferencespopup.cpp +++ b/toonz/sources/toonz/preferencespopup.cpp @@ -270,6 +270,89 @@ Preferences::LevelFormat PreferencesPopup::FormatProperties::levelFormat() return lf; } +//********************************************************************************** +// PreferencesPopup::Display30bitCheckerView implementation +//********************************************************************************** + +PreferencesPopup::Display30bitChecker::GLView::GLView(QWidget* parent, + bool is30bit) + : QOpenGLWidget(parent), m_is30bit(is30bit) { + setFixedSize(500, 100); +#if QT_VERSION >= 0x051000 + if (m_is30bit) setTextureFormat(TGL_TexFmt10); +#endif +} + +void PreferencesPopup::Display30bitChecker::GLView::initializeGL() { + initializeOpenGLFunctions(); + glClear(GL_COLOR_BUFFER_BIT); +} +void PreferencesPopup::Display30bitChecker::GLView::resizeGL(int width, + int height) { + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, 1, 0, 1, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} +void PreferencesPopup::Display30bitChecker::GLView::paintGL() { + initializeOpenGLFunctions(); + glPushMatrix(); + glBegin(GL_QUADS); + glColor3d(0.071, 0.153, 0.0); + glVertex3d(0, 0, 0); + glVertex3d(0, 1, 0); + glColor3d(0.141, 0.239, 0.0); + glVertex3d(1, 1, 0); + glVertex3d(1, 0, 0); + glEnd(); + glPopMatrix(); +} + +//------------------------- + +PreferencesPopup::Display30bitChecker::Display30bitChecker( + PreferencesPopup* parent) + : QDialog(parent) { + setModal(true); + m_currentDefaultFormat = QSurfaceFormat::defaultFormat(); + + setWindowTitle(tr("Check 30bit display availability")); + + QSurfaceFormat sFmt = m_currentDefaultFormat; + sFmt.setRedBufferSize(10); + sFmt.setGreenBufferSize(10); + sFmt.setBlueBufferSize(10); + sFmt.setAlphaBufferSize(2); + QSurfaceFormat::setDefaultFormat(sFmt); + + GLView* view8bit = new GLView(this, false); + GLView* view10bit = new GLView(this, true); + QPushButton* closeBtn = new QPushButton(tr("Close"), this); + QString infoLabel = tr( + "If the lower gradient looks smooth and has no banding compared to the upper gradient,\n\ +30bit display is available in the current configuration."); + + QVBoxLayout* lay = new QVBoxLayout(); + lay->setMargin(10); + lay->setSpacing(10); + { + lay->addWidget(view8bit); + lay->addWidget(view10bit); + lay->addWidget(new QLabel(infoLabel, this)); + lay->addWidget(closeBtn, 0, Qt::AlignCenter); + } + setLayout(lay); + lay->setSizeConstraint(QLayout::SetFixedSize); + + connect(closeBtn, SIGNAL(clicked()), this, SLOT(accept())); +} + +PreferencesPopup::Display30bitChecker::~Display30bitChecker() { + QSurfaceFormat::setDefaultFormat(m_currentDefaultFormat); +} + //********************************************************************************** // PreferencesPopup implementation //********************************************************************************** @@ -691,6 +774,13 @@ void PreferencesPopup::onLutPathChanged() { //----------------------------------------------------------------------------- +void PreferencesPopup::onCheck30bitDisplay() { + Display30bitChecker checker(this); + checker.exec(); +} + +//----------------------------------------------------------------------------- + void PreferencesPopup::onAddLevelFormat() { bool ok = true; QString formatName = DVGui::getText(tr("New Level Format"), @@ -1016,6 +1106,7 @@ QString PreferencesPopup::getUIString(PreferencesItemId id) { {colorCalibrationLutPaths, tr("3DLUT File for [%1]:") .arg(LutManager::instance()->getMonitorName())}, + {displayIn30bit, tr("30bit Display*")}, {showIconsInMenu, tr("Show Icons In Menu*")}, {viewerIndicatorEnabled, tr("Show Viewer Indicators")}, @@ -1456,11 +1547,14 @@ QWidget* PreferencesPopup::createInterfacePage() { for (const QString& name : m_pref->getLanguageList()) languageItemList.push_back(ComboBoxItem(name, name)); + QPushButton* check30bitBtn = new QPushButton(tr("Check Availability")); + QWidget* widget = new QWidget(this); QGridLayout* lay = new QGridLayout(); setupLayout(lay); insertUI(CurrentStyleSheetName, lay, styleSheetItemList); + int row = lay->rowCount(); // lay->addWidget(new QLabel(tr("Icon Theme*:"), this), 2, 0, // Qt::AlignRight | Qt::AlignVCenter); @@ -1488,6 +1582,11 @@ QWidget* PreferencesPopup::createInterfacePage() { // insertUI(interfaceFontStyle, lay, buildFontStyleList()); QGridLayout* colorCalibLay = insertGroupBoxUI(colorCalibrationEnabled, lay); { insertUI(colorCalibrationLutPaths, colorCalibLay); } +#if QT_VERSION >= 0x051000 + insertUI(displayIn30bit, lay); + row = lay->rowCount(); + lay->addWidget(check30bitBtn, row - 1, 3); +#endif // insertUI(showIconsInMenu, lay); lay->setRowStretch(lay->rowCount(), 1); @@ -1504,6 +1603,8 @@ QWidget* PreferencesPopup::createInterfacePage() { ret = ret && connect(TApp::instance()->getCurrentScene(), SIGNAL(pixelUnitSelected(bool)), this, SLOT(onPixelUnitExternallySelected(bool))); + ret = ret && connect(check30bitBtn, SIGNAL(clicked()), this, + SLOT(onCheck30bitDisplay())); assert(ret); m_onEditedFuncMap.insert(CurrentStyleSheetName, diff --git a/toonz/sources/toonz/preferencespopup.h b/toonz/sources/toonz/preferencespopup.h index 44780426..6deaad77 100644 --- a/toonz/sources/toonz/preferencespopup.h +++ b/toonz/sources/toonz/preferencespopup.h @@ -17,6 +17,9 @@ // Qt includes #include #include +#include +#include +#include //============================================================== @@ -65,6 +68,7 @@ public: private: class FormatProperties; + class Display30bitChecker; private: Preferences* m_pref; @@ -173,6 +177,7 @@ private slots: void onPixelUnitExternallySelected(bool on); void onInterfaceFontChanged(const QString& text); void onLutPathChanged(); + void onCheck30bitDisplay(); void onAddLevelFormat(); void onRemoveLevelFormat(); @@ -212,4 +217,36 @@ private slots: void updateEnabledStatus(); }; +//********************************************************************************** +// PreferencesPopup::Display30bitCheckerView definition +//********************************************************************************** + +class PreferencesPopup::Display30bitChecker final : public QDialog { + Q_OBJECT + + QSurfaceFormat m_currentDefaultFormat; + +private: + class GLView; + +public: + Display30bitChecker(PreferencesPopup* parent); + ~Display30bitChecker(); +}; + +class PreferencesPopup::Display30bitChecker::GLView final + : public QOpenGLWidget, + protected QOpenGLFunctions { + Q_OBJECT + bool m_is30bit; + +public: + GLView(QWidget* parent, bool is30bit); + +protected: + void initializeGL() override; + void resizeGL(int width, int height) override; + void paintGL() override; +}; + #endif // PREFERENCESPOPUP_H diff --git a/toonz/sources/toonz/previewfxmanager.cpp b/toonz/sources/toonz/previewfxmanager.cpp index 99c1b3c7..8ee9aea2 100644 --- a/toonz/sources/toonz/previewfxmanager.cpp +++ b/toonz/sources/toonz/previewfxmanager.cpp @@ -1070,7 +1070,9 @@ void PreviewFxInstance::doOnRenderRasterCompleted( /*-- 16bpcで計算された場合、結果をDitheringする --*/ TRasterP rasA = renderData.m_rasA; TRasterP rasB = renderData.m_rasB; - if (rasA->getPixelSize() == 8) // render in 64 bits + // dither the 16bpc image IF the "30bit display" prefernce option is OFF + if (rasA->getPixelSize() == 8 && + !Preferences::instance()->is30bitDisplayEnabled()) // render in 64 bits { TRaster32P auxA(rasA->getLx(), rasA->getLy()); TRop::convert(auxA, rasA); // dithering diff --git a/toonz/sources/toonz/sceneviewer.cpp b/toonz/sources/toonz/sceneviewer.cpp index e372b6dc..0ba7db92 100644 --- a/toonz/sources/toonz/sceneviewer.cpp +++ b/toonz/sources/toonz/sceneviewer.cpp @@ -835,6 +835,10 @@ SceneViewer::SceneViewer(ImageUtils::FullScreenWidget *parent) if (Preferences::instance()->isColorCalibrationEnabled()) m_lutCalibrator = new LutCalibrator(); +#if QT_VERSION >= 0x051000 + if (Preferences::instance()->is30bitDisplayEnabled()) + setTextureFormat(TGL_TexFmt10); +#endif } //----------------------------------------------------------------------------- @@ -1220,8 +1224,14 @@ void SceneViewer::onPreferenceChanged(const QString &prefName) { m_lutCalibrator->initialize(); connect(context(), SIGNAL(aboutToBeDestroyed()), this, SLOT(onContextAboutToBeDestroyed())); - if (m_lutCalibrator->isValid() && !m_fbo) - m_fbo = new QOpenGLFramebufferObject(width(), height()); + if (m_lutCalibrator->isValid() && !m_fbo) { + if (Preferences::instance()->is30bitDisplayEnabled()) { + QOpenGLFramebufferObjectFormat format; + format.setInternalTextureFormat(TGL_TexFmt10); + m_fbo = new QOpenGLFramebufferObject(width(), height(), format); + } else // normally, initialize with GL_RGBA8 format + m_fbo = new QOpenGLFramebufferObject(width(), height()); + } doneCurrent(); } update(); @@ -1279,7 +1289,12 @@ void SceneViewer::resizeGL(int w, int h) { // remake fbo with new size if (m_lutCalibrator && m_lutCalibrator->isValid()) { if (m_fbo) delete m_fbo; - m_fbo = new QOpenGLFramebufferObject(w, h); + if (Preferences::instance()->is30bitDisplayEnabled()) { + QOpenGLFramebufferObjectFormat format; + format.setInternalTextureFormat(TGL_TexFmt10); + m_fbo = new QOpenGLFramebufferObject(w, h, format); + } else // normally, initialize with GL_RGBA8 format + m_fbo = new QOpenGLFramebufferObject(w, h); } // for updating the navigator in levelstrip diff --git a/toonz/sources/toonzlib/imagepainter.cpp b/toonz/sources/toonzlib/imagepainter.cpp index 05d2746f..f76ceb2e 100644 --- a/toonz/sources/toonzlib/imagepainter.cpp +++ b/toonz/sources/toonzlib/imagepainter.cpp @@ -205,7 +205,7 @@ void Painter::flushRasterImages(const TRect &loadbox, double compareX, glDisable(GL_STENCIL_TEST); #ifdef GL_EXT_convolution - if( GLEW_EXT_convolution ) { + if (GLEW_EXT_convolution) { glDisable(GL_CONVOLUTION_1D_EXT); glDisable(GL_CONVOLUTION_2D_EXT); glDisable(GL_SEPARABLE_2D_EXT); @@ -213,14 +213,14 @@ void Painter::flushRasterImages(const TRect &loadbox, double compareX, #endif #ifdef GL_EXT_histogram - if( GLEW_EXT_histogram ) { + if (GLEW_EXT_histogram) { glDisable(GL_HISTOGRAM_EXT); glDisable(GL_MINMAX_EXT); } #endif #ifdef GL_EXT_texture3D - if( GL_EXT_texture3D ) { + if (GL_EXT_texture3D) { glDisable(GL_TEXTURE_3D_EXT); } #endif @@ -363,7 +363,8 @@ void Painter::doFlushRasterImages(const TRasterP &rin, int bg, TRect rect(tfloor(bbox.x0), tfloor(bbox.y0), tceil(bbox.x1), tceil(bbox.y1)); if (rect.isEmpty()) return; - TRaster32P ras; + TRasterP ras; + // TRaster32P ras; TRasterP _rin = rin; TAffine aff; if (m_vSettings.m_useTexture) { @@ -398,6 +399,17 @@ void Painter::doFlushRasterImages(const TRasterP &rin, int bg, // a quickput approximation? } + // when the "30bit display" preference option is enabled, + // image previewed in 16bpc is not dithered & converted to 8bpc, + // but is kept the channel depth as 16bpc. + bool is16bpc = false; + if (_rin->getPixelSize() == 8) { + TRaster64P rasAux(ras->getLx(), ras->getLy()); + TRop::convert(rasAux, ras); + ras = rasAux; + is16bpc = true; + } + ras->lock(); bool showChannelsOnMatte = @@ -433,8 +445,13 @@ void Painter::doFlushRasterImages(const TRasterP &rin, int bg, // Image size is a 0 point. Do nothing if (rect.x0 == rect.x1 && rect.y0 == rect.y1) return; - TRaster32P raux = ras->extract(rect); - raux->fill(bg == 0x40000 ? TPixel::Black : TPixel::White); + if (is16bpc) { + TRaster64P raux = ras->extract(rect); + raux->fill(bg == 0x40000 ? TPixel64::Black : TPixel64::White); + } else { + TRaster32P raux = ras->extract(rect); + raux->fill(bg == 0x40000 ? TPixel::Black : TPixel::White); + } } if (showChannelsOnMatte) @@ -463,8 +480,9 @@ void Painter::doFlushRasterImages(const TRasterP &rin, int bg, glRasterPos2d(rect.x0, rect.y0); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glDrawPixels(ras->getWrap(), ras->getLy(), TGL_FMT, TGL_TYPE, - ras->getRawData()); + glDrawPixels(ras->getWrap(), ras->getLy(), TGL_FMT, + (is16bpc) ? TGL_TYPE16 : TGL_TYPE, + (GLvoid *)ras->getRawData()); CHECK_ERRORS_BY_GL @@ -507,7 +525,7 @@ void Painter::onVectorImage(TVectorImage *vi) { TVectorRenderData rd(m_aff, clipRect, vi->getPalette(), 0, true // alfa enabled - ); + ); if (m_vSettings.m_useChecks) { ToonzCheck *tc = ToonzCheck::instance(); int checks = tc->getChecks(); // &ToonzCheck::eBlackBg @@ -657,8 +675,9 @@ void ImagePainter::paintImage(const TImageP &image, const TDimension &imageSize, // be done on black bg! if (!vimg) painter.flushRasterImages( - loadbox, visualSettings.m_doCompare ? compareSettings.m_compareX - : DefaultCompareValue, + loadbox, + visualSettings.m_doCompare ? compareSettings.m_compareX + : DefaultCompareValue, visualSettings.m_doCompare ? compareSettings.m_compareY : DefaultCompareValue, compareSettings.m_swapCompared); diff --git a/toonz/sources/toonzlib/preferences.cpp b/toonz/sources/toonzlib/preferences.cpp index 76ebf960..35942a06 100644 --- a/toonz/sources/toonzlib/preferences.cpp +++ b/toonz/sources/toonzlib/preferences.cpp @@ -439,6 +439,7 @@ void Preferences::definePreferenceItems() { false); define(colorCalibrationLutPaths, "colorCalibrationLutPaths", QMetaType::QVariantMap, QVariantMap()); + define(displayIn30bit, "displayIn30bit", QMetaType::Bool, false); // hide menu icons by default in macOS since the icon color may not match with // the system color theme