diff --git a/ci-scripts/linux/travis-install.sh b/ci-scripts/linux/travis-install.sh index a004c7dc..b25bf7f2 100644 --- a/ci-scripts/linux/travis-install.sh +++ b/ci-scripts/linux/travis-install.sh @@ -1,6 +1,6 @@ sudo add-apt-repository --yes ppa:beineri/opt-qt551-trusty sudo apt-get update -sudo apt-get install -y liblzo2-dev liblz4-dev libfreetype6-dev libpng-dev libsdl2-dev libglew-dev freeglut3-dev qt55script libsuperlu3-dev libpng-dev qt55svg qt55tools wget libusb-1.0-0-dev libboost-all-dev liblzma-dev +sudo apt-get install -y liblzo2-dev liblz4-dev libfreetype6-dev libpng-dev libsdl2-dev libglew-dev freeglut3-dev qt55script libsuperlu3-dev libpng-dev qt55svg qt55tools qt55multimedia wget libusb-1.0-0-dev libboost-all-dev liblzma-dev # someone forgot to include liblz4.pc with the package, use the version from xenial, as it only depends on libc wget http://mirrors.kernel.org/ubuntu/pool/main/l/lz4/liblz4-1_0.0~r131-2ubuntu2_amd64.deb -O liblz4.deb wget http://mirrors.kernel.org/ubuntu/pool/main/l/lz4/liblz4-dev_0.0~r131-2ubuntu2_amd64.deb -O liblz4-dev.deb diff --git a/stuff/config/loc/日本語/toonz.qm b/stuff/config/loc/日本語/toonz.qm index 5c6d3cd2..50d23e5d 100644 Binary files a/stuff/config/loc/日本語/toonz.qm and b/stuff/config/loc/日本語/toonz.qm differ diff --git a/stuff/config/qss/gray_048/gray_048.less b/stuff/config/qss/gray_048/gray_048.less index 54faea3c..5d0ce3c1 100644 --- a/stuff/config/qss/gray_048/gray_048.less +++ b/stuff/config/qss/gray_048/gray_048.less @@ -1343,4 +1343,8 @@ QDialog #dialogButtonFrame { { .baseBG(light, 30%); } +} + +#LargeSizedText { + font-size: 17px; } \ No newline at end of file diff --git a/stuff/config/qss/gray_048/gray_048.qss b/stuff/config/qss/gray_048/gray_048.qss index dd68b271..dcbe04f1 100644 --- a/stuff/config/qss/gray_048/gray_048.qss +++ b/stuff/config/qss/gray_048/gray_048.qss @@ -1345,5 +1345,8 @@ QDialog #dialogButtonFrame { #MatchLineButton::pressed { background-color: #7d7d7d; } +#LargeSizedText { + font-size: 17px; +} //# sourceMappingURL=gray_048.qss.map \ No newline at end of file diff --git a/stuff/config/qss/gray_048/gray_048_mac.qss b/stuff/config/qss/gray_048/gray_048_mac.qss index f30fc00d..d1dc2d1e 100644 --- a/stuff/config/qss/gray_048/gray_048_mac.qss +++ b/stuff/config/qss/gray_048/gray_048_mac.qss @@ -1345,5 +1345,8 @@ QDialog #dialogButtonFrame { #MatchLineButton::pressed { background-color: #7d7d7d; } +#LargeSizedText { + font-size: 17px; +} //# sourceMappingURL=gray_048_mac.qss.map \ No newline at end of file diff --git a/stuff/config/qss/gray_072/gray_072.less b/stuff/config/qss/gray_072/gray_072.less index 11044b20..4c7fe660 100644 --- a/stuff/config/qss/gray_072/gray_072.less +++ b/stuff/config/qss/gray_072/gray_072.less @@ -1343,4 +1343,8 @@ QDialog #dialogButtonFrame { { .baseBG(light, 30%); } +} + +#LargeSizedText { + font-size: 17px; } \ No newline at end of file diff --git a/stuff/config/qss/gray_072/gray_072.qss b/stuff/config/qss/gray_072/gray_072.qss index 93b1aea2..5d8b18ad 100644 --- a/stuff/config/qss/gray_072/gray_072.qss +++ b/stuff/config/qss/gray_072/gray_072.qss @@ -1345,5 +1345,8 @@ QDialog #dialogButtonFrame { #MatchLineButton::pressed { background-color: #949494; } +#LargeSizedText { + font-size: 17px; +} //# sourceMappingURL=gray_072.qss.map \ No newline at end of file diff --git a/stuff/config/qss/gray_072/gray_072_mac.qss b/stuff/config/qss/gray_072/gray_072_mac.qss index fe324deb..cb96be4a 100644 --- a/stuff/config/qss/gray_072/gray_072_mac.qss +++ b/stuff/config/qss/gray_072/gray_072_mac.qss @@ -1345,5 +1345,8 @@ QDialog #dialogButtonFrame { #MatchLineButton::pressed { background-color: #949494; } +#LargeSizedText { + font-size: 17px; +} //# sourceMappingURL=gray_072_mac.qss.map \ No newline at end of file diff --git a/stuff/config/qss/gray_128/gray_128.less b/stuff/config/qss/gray_128/gray_128.less index 17897ca3..5bbb76bd 100644 --- a/stuff/config/qss/gray_128/gray_128.less +++ b/stuff/config/qss/gray_128/gray_128.less @@ -195,6 +195,19 @@ QCheckBox { } } +QGroupBox { + border: 1px solid; + .set_margin( 5px, 5px ); + .set_padding( 3px, 5px ); + &::title { + subcontrol-origin: margin; + padding: 0px; + margin-top: -4px; + /*bottom: 3px;*/ + left: 15px; + } +} + /* ------ Toonz Classes Difinitions ------ */ /* ------ Palette ------ */ @@ -1157,4 +1170,8 @@ QDialog #dialogButtonFrame { &:hover { background-color: rgb(220,220,220); } +} + +#LargeSizedText { + font-size: 17px; } \ No newline at end of file diff --git a/stuff/config/qss/gray_128/gray_128.qss b/stuff/config/qss/gray_128/gray_128.qss index b12d4597..494ca007 100644 --- a/stuff/config/qss/gray_128/gray_128.qss +++ b/stuff/config/qss/gray_128/gray_128.qss @@ -123,6 +123,24 @@ QCheckBox:hover { QCheckBox:disabled { color: #404040; } +QGroupBox { + border: 1px solid; + margin-left: 5px; + margin-right: 5px; + margin-top: 5px; + margin-bottom: 5px; + padding-left: 3px; + padding-right: 3px; + padding-top: 5px; + padding-bottom: 5px; +} +QGroupBox::title { + subcontrol-origin: margin; + padding: 0px; + margin-top: -4px; + /*bottom: 3px;*/ + left: 15px; +} /* ------ Toonz Classes Difinitions ------ */ /* ------ Palette ------ */ PaletteViewer #ToolBarContainer { @@ -1073,5 +1091,8 @@ QDialog #dialogButtonFrame { #FxSettingsHelpButton:hover { background-color: #dcdcdc; } +#LargeSizedText { + font-size: 17px; +} //# sourceMappingURL=gray_128.qss.map \ No newline at end of file diff --git a/stuff/config/qss/gray_128/gray_128_mac.qss b/stuff/config/qss/gray_128/gray_128_mac.qss index b13f190a..fcac0dee 100644 --- a/stuff/config/qss/gray_128/gray_128_mac.qss +++ b/stuff/config/qss/gray_128/gray_128_mac.qss @@ -123,6 +123,24 @@ QCheckBox:hover { QCheckBox:disabled { color: #404040; } +QGroupBox { + border: 1px solid; + margin-left: 5px; + margin-right: 5px; + margin-top: 5px; + margin-bottom: 5px; + padding-left: 3px; + padding-right: 3px; + padding-top: 5px; + padding-bottom: 5px; +} +QGroupBox::title { + subcontrol-origin: margin; + padding: 0px; + margin-top: -4px; + /*bottom: 3px;*/ + left: 15px; +} /* ------ Toonz Classes Difinitions ------ */ /* ------ Palette ------ */ PaletteViewer #ToolBarContainer { @@ -1073,5 +1091,8 @@ QDialog #dialogButtonFrame { #FxSettingsHelpButton:hover { background-color: #dcdcdc; } +#LargeSizedText { + font-size: 17px; +} //# sourceMappingURL=gray_128_mac.qss.map \ No newline at end of file diff --git a/stuff/profiles/layouts/rooms/StudioGhibli/menubar_template.xml b/stuff/profiles/layouts/rooms/StudioGhibli/menubar_template.xml index 5c84910a..eee1faf2 100644 --- a/stuff/profiles/layouts/rooms/StudioGhibli/menubar_template.xml +++ b/stuff/profiles/layouts/rooms/StudioGhibli/menubar_template.xml @@ -84,6 +84,8 @@ MI_CleanupPreview MI_CameraTest MI_Cleanup + + MI_PencilTest MI_AddFrames diff --git a/stuff/profiles/layouts/rooms/StudioGhibli/room1_menubar.xml b/stuff/profiles/layouts/rooms/StudioGhibli/room1_menubar.xml index 373e4b15..aadc81dd 100644 --- a/stuff/profiles/layouts/rooms/StudioGhibli/room1_menubar.xml +++ b/stuff/profiles/layouts/rooms/StudioGhibli/room1_menubar.xml @@ -25,6 +25,8 @@ MI_Scan MI_SetScanCropbox MI_ResetScanCropbox + + MI_PencilTest MI_CleanupSettings diff --git a/toonz/sources/CMakeLists.txt b/toonz/sources/CMakeLists.txt index a29c4b21..012ce19c 100644 --- a/toonz/sources/CMakeLists.txt +++ b/toonz/sources/CMakeLists.txt @@ -136,7 +136,8 @@ find_package(Qt5 REQUIRED Script Widgets PrintSupport - LinguistTools) + LinguistTools + Multimedia) if(WIN32) include_directories( diff --git a/toonz/sources/common/trop/quickput.cpp b/toonz/sources/common/trop/quickput.cpp index f8900f69..e90c3064 100644 --- a/toonz/sources/common/trop/quickput.cpp +++ b/toonz/sources/common/trop/quickput.cpp @@ -4276,18 +4276,21 @@ void quickPut(const TRasterP &dn, const TRasterP &up, const TAffine &aff, else doQuickPutNoFilter(dn32, up8, aff, colorScale); } else if (dn32 && up32) { - if (areAlmostEqual(aff.a12, 0) && areAlmostEqual(aff.a21, 0)) + if (areAlmostEqual(aff.a12, 0) && areAlmostEqual(aff.a21, 0)){ if (bilinear) doQuickPutFilter(dn32, up32, aff.a11, aff.a22, aff.a13, aff.a23); - else + else { doQuickPutNoFilter(dn32, up32, aff.a11, aff.a22, aff.a13, aff.a23, - colorScale, doPremultiply, whiteTransp, firstColumn, - doRasterDarkenBlendedView); + colorScale, doPremultiply, whiteTransp, firstColumn, + doRasterDarkenBlendedView); + } + } else if (bilinear) doQuickPutFilter(dn32, up32, aff); - else + else { doQuickPutNoFilter(dn32, up32, aff, colorScale, doPremultiply, - whiteTransp, firstColumn, doRasterDarkenBlendedView); + whiteTransp, firstColumn, doRasterDarkenBlendedView); + } } else if (dn32 && up64) doQuickPutNoFilter(dn32, up64, aff, doPremultiply, firstColumn); else diff --git a/toonz/sources/include/tpixelutils.h b/toonz/sources/include/tpixelutils.h index 7f14af57..f8552cb0 100644 --- a/toonz/sources/include/tpixelutils.h +++ b/toonz/sources/include/tpixelutils.h @@ -125,10 +125,11 @@ DVAPI inline T quickOverPixPremultT(const T &bot, const T &top) { (bot.m == max) ? max : max - (max - bot.m) * (max - top.m) / max); } //------------------------------------------------------------------------------------ -/*-- Viewer上でラスタ素材を「比較暗」合成表示する --*/ +/*-- Show raster images darken-blended on the viewer --*/ template DVAPI inline T quickOverPixDarkenBlendedT(const T &bot, const T &top) { - UINT max = T::maxChannelValue; + UINT max = T::maxChannelValue; + if (bot.m == 0) return top; TUINT32 r = (top.r < bot.r) ? top.r : bot.r; TUINT32 g = (top.g < bot.g) ? top.g : bot.g; TUINT32 b = (top.b < bot.b) ? top.b : bot.b; diff --git a/toonz/sources/toonz/CMakeLists.txt b/toonz/sources/toonz/CMakeLists.txt index 052c856b..b15498e9 100644 --- a/toonz/sources/toonz/CMakeLists.txt +++ b/toonz/sources/toonz/CMakeLists.txt @@ -138,6 +138,7 @@ set(MOC_HEADERS comboviewerpane.h historypane.h cleanupsettingspane.h + penciltestpopup.h # Tracker file dummyprocessor.h metnum.h @@ -294,6 +295,7 @@ set(SOURCES comboviewerpane.cpp historypane.cpp cleanupsettingspane.cpp + penciltestpopup.cpp # Tracker file dummyprocessor.cpp metnum.cpp @@ -359,7 +361,7 @@ endif() if(WIN32) target_link_libraries(OpenToonz_${VERSION} - Qt5::WinMain Qt5::Core Qt5::Gui Qt5::Network Qt5::OpenGL Qt5::Svg Qt5::Xml Qt5::Script Qt5::Widgets Qt5::PrintSupport + Qt5::WinMain Qt5::Core Qt5::Gui Qt5::Network Qt5::OpenGL Qt5::Svg Qt5::Xml Qt5::Script Qt5::Widgets Qt5::PrintSupport Qt5::Multimedia ${GL_LIB} ${GLUT_LIB} tnzcore tnzbase toonzlib colorfx tnzext image sound toonzqt tnztools tnzstdfx tfarm) elseif(APPLE) @@ -370,7 +372,7 @@ elseif(APPLE) # 変なところにライブラリ生成するカスども set(EXTRA_LIBS ${EXTRA_LIBS} "$" "$") - target_link_libraries(OpenToonz_${VERSION} Qt5::Core Qt5::Gui Qt5::Network Qt5::OpenGL Qt5::Svg Qt5::Xml Qt5::Script Qt5::Widgets Qt5::PrintSupport ${GL_LIB} ${GLUT_LIB} ${COCOA_LIB} ${EXTRA_LIBS}) + target_link_libraries(OpenToonz_${VERSION} Qt5::Core Qt5::Gui Qt5::Network Qt5::OpenGL Qt5::Svg Qt5::Xml Qt5::Script Qt5::Widgets Qt5::PrintSupport Qt5::Multimedia ${GL_LIB} ${GLUT_LIB} ${COCOA_LIB} ${EXTRA_LIBS}) elseif(UNIX) _find_toonz_library(EXTRA_LIBS "tnzcore;tnzbase;toonzlib;colorfx;tnzext;image;sound;toonzqt;tnztools") @@ -379,7 +381,7 @@ elseif(UNIX) set(EXTRA_LIBS ${EXTRA_LIBS} ${Boost_LIBRARIES} ${OPENBLAS_LIB}) - target_link_libraries(OpenToonz_${VERSION} Qt5::Core Qt5::Gui Qt5::Network Qt5::OpenGL Qt5::Svg Qt5::Xml Qt5::Script Qt5::Widgets Qt5::PrintSupport ${GL_LIB} ${GLUT_LIB} ${GLU_LIB} ${EXTRA_LIBS}) + target_link_libraries(OpenToonz_${VERSION} Qt5::Core Qt5::Gui Qt5::Network Qt5::OpenGL Qt5::Svg Qt5::Xml Qt5::Script Qt5::Widgets Qt5::PrintSupport Qt5::Multimedia ${GL_LIB} ${GLUT_LIB} ${GLU_LIB} ${EXTRA_LIBS}) endif() if(APPLE) diff --git a/toonz/sources/toonz/mainwindow.cpp b/toonz/sources/toonz/mainwindow.cpp index 67e17160..3cb54f63 100644 --- a/toonz/sources/toonz/mainwindow.cpp +++ b/toonz/sources/toonz/mainwindow.cpp @@ -1650,6 +1650,9 @@ void MainWindow::defineActions() { MenuScanCleanupCommandType); createMenuScanCleanupAction(MI_Cleanup, tr("&Cleanup"), ""); + + createMenuScanCleanupAction(MI_PencilTest, tr("&Pencil Test..."), ""); + createMenuLevelAction(MI_AddFrames, tr("&Add Frames..."), ""); createMenuLevelAction(MI_Renumber, tr("&Renumber..."), ""); createMenuLevelAction(MI_ReplaceLevel, tr("&Replace Level..."), ""); diff --git a/toonz/sources/toonz/menubarcommandids.h b/toonz/sources/toonz/menubarcommandids.h index 8daabe2b..a67dc434 100644 --- a/toonz/sources/toonz/menubarcommandids.h +++ b/toonz/sources/toonz/menubarcommandids.h @@ -308,4 +308,5 @@ #define MI_PreviewFx "MI_PreviewFx" #define MI_About "MI_About" +#define MI_PencilTest "MI_PencilTest" #endif diff --git a/toonz/sources/toonz/penciltestpopup.cpp b/toonz/sources/toonz/penciltestpopup.cpp new file mode 100644 index 00000000..ac7dd7f3 --- /dev/null +++ b/toonz/sources/toonz/penciltestpopup.cpp @@ -0,0 +1,993 @@ +#include "penciltestpopup.h" + +// Tnz6 includes +#include "tapp.h" +#include "menubarcommandids.h" +#include "formatsettingspopups.h" +#include "filebrowsermodel.h" + +// TnzQt includes +#include "toonzqt/menubarcommand.h" +#include "toonzqt/filefield.h" +#include "toonzqt/intfield.h" +#include "toonzqt/gutil.h" + +// Tnzlib includes +#include "toonz/tproject.h" +#include "toonz/tscenehandle.h" +#include "toonz/toonzscene.h" +#include "toutputproperties.h" +#include "toonz/sceneproperties.h" +#include "toonz/namebuilder.h" +#include "toonz/levelset.h" +#include "toonz/txshleveltypes.h" +#include "toonz/toonzfolders.h" +#include "toonz/tframehandle.h" +#include "toonz/tcolumnhandle.h" +#include "toonz/txsheethandle.h" +#include "toonz/txshsimplelevel.h" +#include "toonz/levelproperties.h" +#include "toonz/tcamera.h" + +// TnzCore includes +#include "tsystem.h" +#include "tpixelutils.h" + +#include + +// Qt includes +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace DVGui; + +namespace { + +void convertImageToRaster(TRaster32P dstRas, const QImage& srcImg) { + dstRas->lock(); + int lx = dstRas->getLx(); + int ly = dstRas->getLy(); + assert(lx == srcImg.width() && ly == srcImg.height()); + for (int j = 0; j < ly; j++) { + TPixel32* dstPix = dstRas->pixels(j); + for (int i = 0; i < lx; i++, dstPix++) { + QRgb srcPix = srcImg.pixel(lx - 1 - i, j); + dstPix->r = qRed(srcPix); + dstPix->g = qGreen(srcPix); + dstPix->b = qBlue(srcPix); + dstPix->m = TPixel32::maxChannelValue; + } + } + dstRas->unlock(); +} + +void bgReduction(QImage& srcImg, QImage& bgImg, int reduction) { + float reductionRatio = (float)reduction / 100.0f; + // first, make the reduction table + std::vector reductionAmount(256); + for (int i = 0; i < reductionAmount.size(); i++) { + reductionAmount[i] = (int)(std::floor((float)(255 - i) * reductionRatio)); + } + // then, compute for all pixels + int lx = srcImg.width(); + int ly = srcImg.height(); + for (int j = 0; j < ly; j++) { + // TPixel32 * pix = ras->pixels(j); + QRgb* pix = (QRgb*)srcImg.scanLine(j); + QRgb* bgPix = (QRgb*)bgImg.scanLine(j); + for (int i = 0; i < lx; i++, pix++, bgPix++) { + *pix = qRgb(std::min(255, qRed(*pix) + reductionAmount[qRed(*bgPix)]), + std::min(255, qGreen(*pix) + reductionAmount[qGreen(*bgPix)]), + std::min(255, qBlue(*pix) + reductionAmount[qBlue(*bgPix)])); + } + } +} + +// referenced from brightnessandcontrastpopup.cpp +void my_compute_lut(double contrast, double brightness, std::vector& lut) { + const int maxChannelValue = lut.size() - 1; + const double half_maxChannelValueD = 0.5 * maxChannelValue; + const double maxChannelValueD = maxChannelValue; + + int i; + double value, nvalue, power; + + int lutSize = lut.size(); + for (i = 0; i < lutSize; i++) { + value = i / maxChannelValueD; + + // brightness + if (brightness < 0.0) + value = value * (1.0 + brightness); + else + value = value + ((1.0 - value) * brightness); + + // contrast + if (contrast < 0.0) { + if (value > 0.5) + nvalue = 1.0 - value; + else + nvalue = value; + if (nvalue < 0.0) nvalue = 0.0; + nvalue = 0.5 * pow(nvalue * 2.0, (double)(1.0 + contrast)); + if (value > 0.5) + value = 1.0 - nvalue; + else + value = nvalue; + } else { + if (value > 0.5) + nvalue = 1.0 - value; + else + nvalue = value; + if (nvalue < 0.0) nvalue = 0.0; + power = + (contrast == 1.0) ? half_maxChannelValueD : 1.0 / (1.0 - contrast); + nvalue = 0.5 * pow(2.0 * nvalue, power); + if (value > 0.5) + value = 1.0 - nvalue; + else + value = nvalue; + } + + lut[i] = value * maxChannelValueD; + } +} + +//----------------------------------------------------------------------------- + +inline void doPixGray(QRgb* pix, const std::vector& lut) { + int gray = qGray(qRgb(lut[qRed(*pix)], lut[qGreen(*pix)], lut[qBlue(*pix)])); + *pix = qRgb(gray, gray, gray); +} + +//----------------------------------------------------------------------------- + +inline void doPixBinary(QRgb* pix, const std::vector& lut, + unsigned char threshold) { + int gray = qGray(qRgb(lut[qRed(*pix)], lut[qGreen(*pix)], lut[qBlue(*pix)])); + if ((unsigned char)gray >= threshold) + gray = 255; + else + gray = 0; + *pix = qRgb(gray, gray, gray); +} + +//----------------------------------------------------------------------------- + +inline void doPix(QRgb* pix, const std::vector& lut) { + // The captured image MUST be full opaque! + *pix = qRgb(lut[qRed(*pix)], lut[qGreen(*pix)], lut[qBlue(*pix)]); +} + +//----------------------------------------------------------------------------- + +void onChange(QImage& img, int contrast, int brightness, bool doGray, + unsigned char threshold = 0) { + double b = brightness / 127.0; + double c = contrast / 127.0; + if (c > 1) c = 1; + if (c < -1) c = -1; + + std::vector lut(TPixel32::maxChannelValue + 1); + my_compute_lut(c, b, lut); + + int lx = img.width(), y, ly = img.height(); + + if (doGray) { + if (threshold == 0) { // Grayscale + for (y = 0; y < ly; ++y) { + QRgb *pix = (QRgb *)img.scanLine(y), *endPix = (QRgb *)(pix + lx); + while (pix < endPix) { + doPixGray(pix, lut); + ++pix; + } + } + } else { // Binary + for (y = 0; y < ly; ++y) { + QRgb *pix = (QRgb *)img.scanLine(y), *endPix = (QRgb *)(pix + lx); + while (pix < endPix) { + doPixBinary(pix, lut, threshold); + ++pix; + } + } + } + } else { // color + for (y = 0; y < ly; ++y) { + QRgb *pix = (QRgb *)img.scanLine(y), *endPix = (QRgb *)(pix + lx); + while (pix < endPix) { + doPix(pix, lut); + ++pix; + } + } + } +} + +//----------------------------------------------------------------------------- + +TPointD getCurrentCameraDpi() { + TCamera* camera = + TApp::instance()->getCurrentScene()->getScene()->getCurrentCamera(); + TDimensionD size = camera->getSize(); + TDimension res = camera->getRes(); + return TPointD(res.lx / size.lx, res.ly / size.ly); +} + +} // namespace + +//============================================================================= + +MyViewFinder::MyViewFinder(QWidget* parent) + : QFrame(parent) + , m_image(QImage()) + , m_camera(0) + , m_showOnionSkin(false) + , m_onionOpacity(128) + , m_upsideDown(false) {} + +void MyViewFinder::paintEvent(QPaintEvent* event) { + QPainter p(this); + + p.fillRect(rect(), Qt::black); + + if (m_image.isNull()) { + p.setPen(Qt::white); + QFont font = p.font(); + font.setPixelSize(30); + p.setFont(font); + p.drawText(rect(), Qt::AlignCenter, tr("Camera is not available")); + return; + } + + if (m_upsideDown) { + p.translate(m_imageRect.center()); + p.rotate(180); + p.translate(-m_imageRect.center()); + } + + p.drawImage(m_imageRect, m_image); + + if (m_showOnionSkin && m_onionOpacity > 0.0f && !m_previousImage.isNull() && + m_previousImage.size() == m_image.size()) { + p.setCompositionMode(QPainter::CompositionMode_DestinationIn); + p.setPen(Qt::NoPen); + p.setBrush(QBrush(QColor(255, 255, 255, 255 - m_onionOpacity))); + p.drawRect(m_imageRect); + p.setCompositionMode(QPainter::CompositionMode_DestinationOver); + p.drawImage(m_imageRect, m_previousImage); + } +} + +void MyViewFinder::resizeEvent(QResizeEvent* event) { + if (!m_camera) return; + QSize cameraReso = m_camera->viewfinderSettings().resolution(); + double cameraAR = (double)cameraReso.width() / (double)cameraReso.height(); + // in case the camera aspect is wider than this widget + if (cameraAR >= (double)width() / (double)height()) { + m_imageRect.setWidth(width()); + m_imageRect.setHeight((int)((double)width() / cameraAR)); + m_imageRect.moveTo(0, (height() - m_imageRect.height()) / 2); + } + // in case the camera aspect is thinner than this widget + else { + m_imageRect.setHeight(height()); + m_imageRect.setWidth((int)((double)height() * cameraAR)); + m_imageRect.moveTo((width() - m_imageRect.width()) / 2, 0); + } +} + +//============================================================================= + +PencilTestPopup::PencilTestPopup() + : Dialog(TApp::instance()->getMainWindow(), false, false, "PencilTest") + , m_currentCamera(0) + , m_cameraImageCapture(0) + , m_captureWhiteBGCue(false) + , m_captureCue(false) { + setWindowTitle(tr("Pencil Test")); + layout()->setSizeConstraint(QLayout::SetNoConstraint); + + std::wstring dateTime = + QDateTime::currentDateTime().toString("yyMMddhhmmss").toStdWString(); + TFilePath cacheImageFp = ToonzFolder::getCacheRootFolder() + + TFilePath(L"penciltest" + dateTime + L".jpg"); + m_cacheImagePath = cacheImageFp.getQString(); + + m_cameraViewfinder = new MyViewFinder(this); + // CameraViewfinderContainer* cvfContainer = new + // CameraViewfinderContainer(m_cameraViewfinder, this); + + m_cameraListCombo = new QComboBox(this); + QPushButton* refreshCamListButton = new QPushButton(tr("Refresh"), this); + m_resolutionCombo = new QComboBox(this); + + QGroupBox* fileFrame = new QGroupBox(tr("File"), this); + m_levelNameEdit = new QLineEdit(this); + m_frameNumberEdit = new IntLineEdit(this, 1, 1, INT_MAX, 4); + m_fileTypeCombo = new QComboBox(this); + m_fileFormatOptionButton = new QPushButton(tr("Options"), this); + m_saveInFileFld = new FileField( + 0, QString("+%1").arg(QString::fromStdString(TProject::Extras))); + QToolButton* nextLevelButton = new QToolButton(this); + + QGroupBox* imageFrame = new QGroupBox(tr("Image adjust"), this); + m_colorTypeCombo = new QComboBox(this); + + m_thresholdFld = new IntField(this); + m_contrastFld = new IntField(this); + m_brightnessFld = new IntField(this); + m_upsideDownCB = new QCheckBox(tr("Upside down"), this); + + m_bgReductionFld = new IntField(this); + m_captureWhiteBGButton = new QPushButton(tr("Capture white BG"), this); + + QGroupBox* displayFrame = new QGroupBox(tr("Display"), this); + m_onionSkinCB = new QCheckBox(tr("Show onion skin"), this); + m_onionOpacityFld = new IntField(this); + + QPushButton* captureButton = new QPushButton(tr("Capture"), this); + QPushButton* closeButton = new QPushButton(tr("Close"), this); + //---- + + m_resolutionCombo->setMaximumWidth(fontMetrics().width("0000 x 0000") + 25); + m_fileTypeCombo->addItems({"jpg", "png", "tga", "tif"}); + m_fileTypeCombo->setCurrentIndex(1); + + fileFrame->setObjectName("CleanupSettingsFrame"); + // Exclude all character which cannot fit in a filepath (Win). + // Dots are also prohibited since they are internally managed by Toonz. + QRegExp rx("[^\\\\/:?*.\"<>|]+"); + m_levelNameEdit->setValidator(new QRegExpValidator(rx, this)); + m_levelNameEdit->setObjectName("LargeSizedText"); + m_frameNumberEdit->setObjectName("LargeSizedText"); + nextLevelButton->setFixedSize(24, 24); + nextLevelButton->setArrowType(Qt::RightArrow); + nextLevelButton->setToolTip(tr("Next Level")); + + imageFrame->setObjectName("CleanupSettingsFrame"); + m_colorTypeCombo->addItems({"Color", "Grayscale", "Black & White"}); + m_colorTypeCombo->setCurrentIndex(0); + m_thresholdFld->setRange(1, 255); + m_thresholdFld->setValue(128); + m_thresholdFld->setDisabled(true); + m_contrastFld->setRange(-127, 127); + m_contrastFld->setValue(0); + m_brightnessFld->setRange(-127, 127); + m_brightnessFld->setValue(0); + m_upsideDownCB->setChecked(false); + + m_bgReductionFld->setRange(0, 100); + m_bgReductionFld->setValue(0); + m_bgReductionFld->setDisabled(true); + + displayFrame->setObjectName("CleanupSettingsFrame"); + m_onionSkinCB->setChecked(false); + m_onionOpacityFld->setRange(1, 100); + m_onionOpacityFld->setValue(50); + m_onionOpacityFld->setDisabled(true); + + captureButton->setObjectName("LargeSizedText"); + captureButton->setFixedHeight(80); + + //---- layout ---- + QHBoxLayout* mainLay = new QHBoxLayout(); + mainLay->setMargin(0); + mainLay->setSpacing(10); + { + QVBoxLayout* leftLay = new QVBoxLayout(); + leftLay->setMargin(5); + leftLay->setSpacing(10); + { + QHBoxLayout* camLay = new QHBoxLayout(); + camLay->setMargin(0); + camLay->setSpacing(3); + { + camLay->addWidget(new QLabel(tr("Camera:"), this), 0); + camLay->addWidget(m_cameraListCombo, 1); + camLay->addWidget(refreshCamListButton, 0); + camLay->addSpacing(10); + camLay->addWidget(new QLabel(tr("Resolution:"), this), 0); + camLay->addWidget(m_resolutionCombo, 1); + camLay->addStretch(0); + } + leftLay->addLayout(camLay, 0); + leftLay->addWidget(m_cameraViewfinder, 1); + } + mainLay->addLayout(leftLay, 1); + + QVBoxLayout* rightLay = new QVBoxLayout(); + rightLay->setMargin(0); + rightLay->setSpacing(5); + { + QVBoxLayout* fileLay = new QVBoxLayout(); + fileLay->setMargin(10); + fileLay->setSpacing(10); + { + QGridLayout* levelLay = new QGridLayout(); + levelLay->setMargin(0); + levelLay->setHorizontalSpacing(3); + levelLay->setVerticalSpacing(10); + { + levelLay->addWidget(new QLabel(tr("Name:"), this), 0, 0, + Qt::AlignRight); + levelLay->addWidget(m_levelNameEdit, 0, 1); + levelLay->addWidget(nextLevelButton, 0, 2); + + levelLay->addWidget(new QLabel(tr("Frame:"), this), 1, 0, + Qt::AlignRight); + levelLay->addWidget(m_frameNumberEdit, 1, 1); + } + levelLay->setColumnStretch(0, 0); + levelLay->setColumnStretch(1, 1); + levelLay->setColumnStretch(2, 0); + fileLay->addLayout(levelLay, 0); + + QHBoxLayout* fileTypeLay = new QHBoxLayout(); + fileTypeLay->setMargin(0); + fileTypeLay->setSpacing(3); + { + fileTypeLay->addWidget(new QLabel(tr("File Type:"), this), 0); + fileTypeLay->addWidget(m_fileTypeCombo, 1); + fileTypeLay->addSpacing(10); + fileTypeLay->addWidget(m_fileFormatOptionButton); + } + fileLay->addLayout(fileTypeLay, 0); + + QHBoxLayout* saveInLay = new QHBoxLayout(); + saveInLay->setMargin(0); + saveInLay->setSpacing(3); + { + saveInLay->addWidget(new QLabel(tr("Save In:"), this), 0); + saveInLay->addWidget(m_saveInFileFld, 1); + } + fileLay->addLayout(saveInLay, 0); + } + fileFrame->setLayout(fileLay); + rightLay->addWidget(fileFrame, 0); + + QGridLayout* imageLay = new QGridLayout(); + imageLay->setMargin(10); + imageLay->setHorizontalSpacing(3); + imageLay->setVerticalSpacing(10); + { + imageLay->addWidget(new QLabel(tr("Color type:"), this), 0, 0, + Qt::AlignRight); + imageLay->addWidget(m_colorTypeCombo, 0, 1); + + imageLay->addWidget(new QLabel(tr("Threshold:"), this), 1, 0, + Qt::AlignRight); + imageLay->addWidget(m_thresholdFld, 1, 1, 1, 2); + + imageLay->addWidget(new QLabel(tr("Contrast:"), this), 2, 0, + Qt::AlignRight); + imageLay->addWidget(m_contrastFld, 2, 1, 1, 2); + + imageLay->addWidget(new QLabel(tr("Brightness:"), this), 3, 0, + Qt::AlignRight); + imageLay->addWidget(m_brightnessFld, 3, 1, 1, 2); + + imageLay->addWidget(m_upsideDownCB, 4, 0, 1, 3, Qt::AlignLeft); + + imageLay->addWidget(new QLabel(tr("BG reduction:"), this), 5, 0, + Qt::AlignRight); + imageLay->addWidget(m_bgReductionFld, 5, 1, 1, 2); + + imageLay->addWidget(m_captureWhiteBGButton, 6, 0, 1, 3); + } + imageLay->setColumnStretch(0, 0); + imageLay->setColumnStretch(1, 0); + imageLay->setColumnStretch(2, 1); + imageFrame->setLayout(imageLay); + rightLay->addWidget(imageFrame, 0); + + QGridLayout* displayLay = new QGridLayout(); + displayLay->setMargin(10); + displayLay->setHorizontalSpacing(3); + displayLay->setVerticalSpacing(10); + { + displayLay->addWidget(m_onionSkinCB, 0, 0, 1, 3); + + displayLay->addWidget(new QLabel(tr("Opacity(%):"), this), 1, 0, + Qt::AlignRight); + displayLay->addWidget(m_onionOpacityFld, 1, 1, 1, 2); + } + displayLay->setColumnStretch(0, 0); + displayLay->setColumnStretch(1, 0); + displayLay->setColumnStretch(2, 1); + displayFrame->setLayout(displayLay); + rightLay->addWidget(displayFrame); + + rightLay->addStretch(1); + + rightLay->addWidget(captureButton, 0); + rightLay->addSpacing(20); + rightLay->addWidget(closeButton, 0); + } + mainLay->addLayout(rightLay, 0); + } + m_topLayout->addLayout(mainLay); + + //---- signal-slot connections ---- + bool ret = true; + ret = ret && connect(refreshCamListButton, SIGNAL(pressed()), this, + SLOT(refreshCameraList())); + ret = ret && connect(m_cameraListCombo, SIGNAL(activated(int)), this, + SLOT(onCameraListComboActivated(int))); + ret = ret && connect(m_resolutionCombo, SIGNAL(activated(const QString&)), + this, SLOT(onResolutionComboActivated(const QString&))); + ret = ret && connect(m_fileFormatOptionButton, SIGNAL(pressed()), this, + SLOT(onFileFormatOptionButtonPressed())); + ret = ret && + connect(nextLevelButton, SIGNAL(pressed()), this, SLOT(onNextName())); + ret = ret && connect(m_colorTypeCombo, SIGNAL(currentIndexChanged(int)), this, + SLOT(onColorTypeComboChanged(int))); + ret = ret && connect(m_captureWhiteBGButton, SIGNAL(pressed()), this, + SLOT(onCaptureWhiteBGButtonPressed())); + ret = ret && connect(m_onionSkinCB, SIGNAL(toggled(bool)), this, + SLOT(onOnionCBToggled(bool))); + ret = ret && connect(m_onionOpacityFld, SIGNAL(valueEditedByHand()), this, + SLOT(onOnionOpacityFldEdited())); + ret = ret && connect(m_upsideDownCB, SIGNAL(toggled(bool)), + m_cameraViewfinder, SLOT(onUpsideDownChecked(bool))); + ret = ret && connect(closeButton, SIGNAL(clicked()), this, SLOT(reject())); + ret = + ret && connect(captureButton, SIGNAL(clicked()), this, SLOT(onCapture())); + assert(ret); + + refreshCameraList(); + onNextName(); +} + +//----------------------------------------------------------------------------- + +PencilTestPopup::~PencilTestPopup() { + if (m_currentCamera && m_currentCamera->state() == QCamera::ActiveState) + m_currentCamera->stop(); + // remove the cache image, if it exists + TFilePath fp(m_cacheImagePath); + if (TFileStatus(fp).doesExist()) TSystem::deleteFile(fp); +} + +//----------------------------------------------------------------------------- + +void PencilTestPopup::refreshCameraList() { + m_cameraListCombo->clear(); + + QList cameras = QCameraInfo::availableCameras(); + if (cameras.empty()) { + m_cameraListCombo->addItem(tr("No camera found")); + m_cameraListCombo->setMaximumWidth(250); + m_cameraListCombo->setDisabled(true); + } + + int maxTextLength = 0; + int defaultIndex; + for (int c = 0; c < cameras.size(); c++) { + QString camDesc = cameras.at(c).description(); + m_cameraListCombo->addItem(camDesc); + maxTextLength = std::max(maxTextLength, fontMetrics().width(camDesc)); + if (cameras.at(c).deviceName() == QCameraInfo::defaultCamera().deviceName()) + defaultIndex = c; + } + m_cameraListCombo->setMaximumWidth(maxTextLength + 25); + m_cameraListCombo->setEnabled(true); + m_cameraListCombo->setCurrentIndex(defaultIndex); + + onCameraListComboActivated(defaultIndex); +} + +//----------------------------------------------------------------------------- + +void PencilTestPopup::onCameraListComboActivated(int index) { + QList cameras = QCameraInfo::availableCameras(); + if (cameras.size() != m_cameraListCombo->count()) return; + + // in case the camera is not changed (just click the combobox) + if (cameras.at(index).deviceName() == m_deviceName) return; + + QCamera* oldCamera = m_currentCamera; + m_currentCamera = new QCamera(cameras.at(index), this); + m_deviceName = cameras.at(index).deviceName(); + if (m_cameraImageCapture) { + disconnect(m_cameraImageCapture, SIGNAL(imageCaptured(int, const QImage&)), + this, SLOT(onImageCaptured(int, const QImage&))); + delete m_cameraImageCapture; + } + m_cameraImageCapture = new QCameraImageCapture(m_currentCamera, this); + /* Capturing to buffer currently seems not to be supported on Windows */ + // if + // (!m_cameraImageCapture->isCaptureDestinationSupported(QCameraImageCapture::CaptureToBuffer)) + // std::cout << "it does not support CaptureToBuffer" << std::endl; + m_cameraImageCapture->setCaptureDestination( + QCameraImageCapture::CaptureToBuffer); + connect(m_cameraImageCapture, SIGNAL(imageCaptured(int, const QImage&)), this, + SLOT(onImageCaptured(int, const QImage&))); + + // loading new camera + m_currentCamera->load(); + + // refresh resolution + m_resolutionCombo->clear(); + QList sizes = m_currentCamera->supportedViewfinderResolutions(); + + for (int s = 0; s < sizes.size(); s++) { + m_resolutionCombo->addItem( + QString("%1 x %2").arg(sizes.at(s).width()).arg(sizes.at(s).height())); + } + if (!sizes.isEmpty()) { + m_resolutionCombo->setCurrentIndex(0); + QCameraViewfinderSettings settings = m_currentCamera->viewfinderSettings(); + settings.setResolution(sizes[0]); + m_currentCamera->setViewfinderSettings(settings); + QImageEncoderSettings imageEncoderSettings; + imageEncoderSettings.setCodec("PNG"); + imageEncoderSettings.setResolution(sizes[0]); + m_cameraImageCapture->setEncodingSettings(imageEncoderSettings); + } + m_cameraViewfinder->setCamera(m_currentCamera); + + // deleting old camera + if (oldCamera) { + if (oldCamera->state() == QCamera::ActiveState) oldCamera->stop(); + delete oldCamera; + } + // start new camera + m_currentCamera->start(); + m_cameraViewfinder->setImage(QImage()); +} + +//----------------------------------------------------------------------------- + +void PencilTestPopup::onResolutionComboActivated(const QString& itemText) { + // resolution is written in the itemText with the format " x " + // (e.g. "800 x 600") + QStringList texts = itemText.split(' '); + // the splited text must be "" "x" and "" + if (texts.size() != 3) return; + + m_currentCamera->stop(); + m_currentCamera->unload(); + QCameraViewfinderSettings settings = m_currentCamera->viewfinderSettings(); + QSize newResolution(texts[0].toInt(), texts[2].toInt()); + settings.setResolution(newResolution); + m_currentCamera->setViewfinderSettings(settings); + QImageEncoderSettings imageEncoderSettings; + imageEncoderSettings.setCodec("image/jpeg"); + imageEncoderSettings.setQuality(QMultimedia::NormalQuality); + imageEncoderSettings.setResolution(newResolution); + m_cameraImageCapture->setEncodingSettings(imageEncoderSettings); + + // reset white bg + m_whiteBGImg = QImage(); + m_bgReductionFld->setDisabled(true); + + m_currentCamera->start(); + m_cameraViewfinder->setImage(QImage()); +} + +//----------------------------------------------------------------------------- + +void PencilTestPopup::onFileFormatOptionButtonPressed() { + if (m_fileTypeCombo->currentIndex() == 0) return; + // Tentatively use the preview output settings + ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene(); + if (!scene) return; + TOutputProperties* prop = scene->getProperties()->getPreviewProperties(); + std::string ext = m_fileTypeCombo->currentText().toStdString(); + openFormatSettingsPopup(this, ext, prop->getFileFormatProperties(ext)); +} + +//----------------------------------------------------------------------------- + +void PencilTestPopup::onNextName() { + const std::auto_ptr nameBuilder(NameBuilder::getBuilder(L"")); + + TLevelSet* levelSet = + TApp::instance()->getCurrentScene()->getScene()->getLevelSet(); + ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene(); + std::wstring levelName = L""; + + // Select a different unique level name in case it already exists (either in + // scene or on disk) + TFilePath fp; + TFilePath actualFp; + for (;;) { + levelName = nameBuilder->getNext(); + + if (levelSet->getLevel(levelName) != 0) continue; + + fp = TFilePath(m_saveInFileFld->getPath()) + + TFilePath(levelName + L".." + + m_fileTypeCombo->currentText().toStdWString()); + actualFp = scene->decodeFilePath(fp); + + if (TSystem::doesExistFileOrLevel(actualFp)) { + continue; + } + + break; + } + + m_levelNameEdit->setText(QString::fromStdWString(levelName)); + m_frameNumberEdit->setValue(1); +} + +//----------------------------------------------------------------------------- + +void PencilTestPopup::onColorTypeComboChanged(int index) { + m_thresholdFld->setEnabled(index == 2); +} + +//----------------------------------------------------------------------------- + +void PencilTestPopup::onImageCaptured(int id, const QImage& image) { + // capture the white BG + if (m_captureWhiteBGCue) { + m_whiteBGImg = image.copy(); + m_captureWhiteBGCue = false; + m_bgReductionFld->setEnabled(true); + } + + QImage procImg = image.copy(); + processImage(procImg); + m_cameraViewfinder->setImage(procImg); + + if (m_captureCue) { + m_captureCue = false; + if (importImage(procImg)) { + m_cameraViewfinder->setPreviousImage(procImg); + m_frameNumberEdit->setValue(m_frameNumberEdit->getValue() + 1); + } + } +} + +//----------------------------------------------------------------------------- + +void PencilTestPopup::timerEvent(QTimerEvent* event) { + if (!m_currentCamera || !m_cameraImageCapture || + !m_cameraImageCapture->isAvailable() || + !m_cameraImageCapture->isReadyForCapture()) + return; + + m_currentCamera->setCaptureMode(QCamera::CaptureStillImage); + m_currentCamera->start(); + m_currentCamera->searchAndLock(); + m_cameraImageCapture->capture(m_cacheImagePath); + m_currentCamera->unlock(); +} + +//----------------------------------------------------------------------------- + +void PencilTestPopup::showEvent(QShowEvent* event) { + m_timerId = startTimer(10); +} + +//----------------------------------------------------------------------------- + +void PencilTestPopup::hideEvent(QHideEvent* event) { killTimer(m_timerId); } + +//----------------------------------------------------------------------------- + +void PencilTestPopup::processImage(QImage& image) { + /* "upside down" is not executed here. It will be done when capturing the + * image */ + + // white bg reduction + if (!m_whiteBGImg.isNull() && m_bgReductionFld->getValue() != 0) { + bgReduction(image, m_whiteBGImg, m_bgReductionFld->getValue()); + } + + int threshold = + (m_colorTypeCombo->currentIndex() != 2) ? 0 : m_thresholdFld->getValue(); + onChange(image, m_contrastFld->getValue(), m_brightnessFld->getValue(), + m_colorTypeCombo->currentIndex() != 0, threshold); +} + +//----------------------------------------------------------------------------- + +void PencilTestPopup::onCaptureWhiteBGButtonPressed() { + m_captureWhiteBGCue = true; +} + +//----------------------------------------------------------------------------- + +void PencilTestPopup::onOnionCBToggled(bool on) { + m_cameraViewfinder->setShowOnionSkin(on); + m_onionOpacityFld->setEnabled(on); +} + +//----------------------------------------------------------------------------- + +void PencilTestPopup::onOnionOpacityFldEdited() { + int value = (int)(255.0f * (float)m_onionOpacityFld->getValue() / 100.0f); + m_cameraViewfinder->setOnionOpacity(value); +} + +//----------------------------------------------------------------------------- + +void PencilTestPopup::onCapture() { m_captureCue = true; } + +//----------------------------------------------------------------------------- +/*! referenced from LevelCreatePopup::apply() +*/ +bool PencilTestPopup::importImage(QImage& image) { + TApp* app = TApp::instance(); + ToonzScene* scene = app->getCurrentScene()->getScene(); + TXsheet* xsh = scene->getXsheet(); + + std::wstring levelName = m_levelNameEdit->text().toStdWString(); + if (levelName.empty()) { + error(tr("No level name specified: please choose a valid level name")); + return false; + } + + int frameNumber = m_frameNumberEdit->getValue(); + + /* create parent directory if it does not exist */ + TFilePath parentDir = + scene->decodeFilePath(TFilePath(m_saveInFileFld->getPath())); + if (!TFileStatus(parentDir).doesExist()) { + QString question; + question = tr("Folder %1 doesn't exist.\nDo you want to create it?") + .arg(toQString(parentDir)); + int ret = DVGui::MsgBox(question, QObject::tr("Yes"), QObject::tr("No")); + if (ret == 0 || ret == 2) return false; + try { + TSystem::mkDir(parentDir); + DvDirModel::instance()->refreshFolder(parentDir.getParentDir()); + } catch (...) { + error(tr("Unable to create") + toQString(parentDir)); + return false; + } + } + + TFilePath levelFp = TFilePath(m_saveInFileFld->getPath()) + + TFilePath(levelName + L".." + + m_fileTypeCombo->currentText().toStdWString()); + TFilePath actualLevelFp = scene->decodeFilePath(levelFp); + + TXshSimpleLevel* sl = 0; + + TXshLevel* level = scene->getLevelSet()->getLevel(levelName); + /* if the level already exists in the scene cast */ + if (level) { + /* if the existing level is not a raster level, then return */ + if (level->getType() != OVL_XSHLEVEL) { + error( + tr("The level name specified is already used: please choose a " + "different level name.")); + return false; + } + /* if the existing level does not match file path and pixel size, then + * return */ + sl = level->getSimpleLevel(); + if (scene->decodeFilePath(sl->getPath()) != actualLevelFp) { + error( + tr("The save in path specified does not match with the existing " + "level.")); + return false; + } + if (sl->getProperties()->getImageRes() != + TDimension(image.width(), image.height())) { + error(tr( + "The captured image size does not match with the existing level.")); + return false; + } + /* if the level already have the same frame, then ask if overwrite it */ + TFilePath frameFp(actualLevelFp.withFrame(frameNumber)); + if (TFileStatus(frameFp).doesExist()) { + QString question = tr("File %1 does exist.\nDo you want to overwrite it?") + .arg(toQString(frameFp)); + int ret = DVGui::MsgBox(question, QObject::tr("Overwrite"), + QObject::tr("Cancel")); + if (ret == 0 || ret == 2) return false; + } + } + /* if the level does not exist in the scene cast */ + else { + /* if the file does exist, load it first */ + if (TFileStatus(actualLevelFp).doesExist()) { + level = scene->loadLevel(actualLevelFp); + if (!level) { + error(tr("Failed to load %1.").arg(toQString(actualLevelFp))); + return false; + } + + /* if the loaded level does not match in pixel size, then return */ + sl = level->getSimpleLevel(); + if (!sl || + sl->getProperties()->getImageRes() != + TDimension(image.width(), image.height())) { + error(tr( + "The captured image size does not match with the existing level.")); + return false; + } + } + /* if the file does not exist, then create a new level */ + else { + TXshLevel* level = scene->createNewLevel(OVL_XSHLEVEL, levelName, + TDimension(), 0, levelFp); + sl = level->getSimpleLevel(); + sl->setPath(levelFp, true); + sl->getProperties()->setDpiPolicy(LevelProperties::DP_CustomDpi); + TPointD currentCamDpi = getCurrentCameraDpi(); + sl->getProperties()->setDpi(currentCamDpi.x); + sl->getProperties()->setImageDpi(currentCamDpi); + sl->getProperties()->setImageRes( + TDimension(image.width(), image.height())); + } + } + + TFrameId fid(frameNumber); + TPointD levelDpi = sl->getDpi(); + /* create the raster */ + TRaster32P raster(image.width(), image.height()); + convertImageToRaster(raster, (m_upsideDownCB->isChecked()) + ? image + : image.mirrored(true, true)); + TRasterImageP ri(raster); + ri->setDpi(levelDpi.x, levelDpi.y); + /* setting the frame */ + sl->setFrame(fid, ri); + + /* set dirty flag */ + sl->getProperties()->setDirtyFlag(true); + + /* placement in xsheet */ + int row = app->getCurrentFrame()->getFrame(); + int col = app->getCurrentColumn()->getColumnIndex(); + + /* try to find the vacant cell */ + int tmpRow = row; + bool isFoundEmptyCell = false; + while (1) { + if (xsh->getCell(tmpRow, col).isEmpty()) { + isFoundEmptyCell = true; + break; + } + if (xsh->getCell(tmpRow, col).m_level->getSimpleLevel() != sl) break; + tmpRow++; + } + + /* in case setting the same level as the the current column */ + if (isFoundEmptyCell) { + xsh->setCell(tmpRow, col, TXshCell(sl, fid)); + } + /* in case the level is different from the current column, then insert a new + column */ + else { + col += 1; + xsh->insertColumn(col); + xsh->setCell(row, col, TXshCell(sl, fid)); + app->getCurrentColumn()->setColumnIndex(col); + } + /* notify */ + app->getCurrentScene()->notifySceneChanged(); + app->getCurrentScene()->notifyCastChange(); + app->getCurrentXsheet()->notifyXsheetChanged(); + + return true; +} + +//----------------------------------------------------------------------------- + +OpenPopupCommandHandler openPencilTestPopup(MI_PencilTest); \ No newline at end of file diff --git a/toonz/sources/toonz/penciltestpopup.h b/toonz/sources/toonz/penciltestpopup.h new file mode 100644 index 00000000..c0170605 --- /dev/null +++ b/toonz/sources/toonz/penciltestpopup.h @@ -0,0 +1,117 @@ +#pragma once + +#ifndef PENCILTESTPOPUP_H +#define PENCILTESTPOPUP_H + +#include "toonzqt/dvdialog.h" + +#include + +// forward decl. +class QCamera; +class QCameraImageCapture; + +class QComboBox; +class QLineEdit; +class QSlider; +class QCheckBox; +class QPushButton; +class QVideoFrame; + +namespace DVGui { +class FileField; +class IntLineEdit; +class IntField; +} + +//============================================================================= +// MyViewFinder +//----------------------------------------------------------------------------- + +class MyViewFinder : public QFrame { + Q_OBJECT + + QImage m_image; + QImage m_previousImage; + QCamera* m_camera; + QRect m_imageRect; + + bool m_showOnionSkin; + int m_onionOpacity; + bool m_upsideDown; + +public: + MyViewFinder(QWidget* parent = 0); + void setImage(const QImage& image) { + m_image = image; + update(); + } + void setCamera(QCamera* camera) { m_camera = camera; } + void setShowOnionSkin(bool on) { m_showOnionSkin = on; } + void setOnionOpacity(int value) { m_onionOpacity = value; } + void setPreviousImage(QImage& prevImage) { m_previousImage = prevImage; } + +protected: + void paintEvent(QPaintEvent* event); + void resizeEvent(QResizeEvent* event); +protected slots: + void onUpsideDownChecked(bool on) { m_upsideDown = on; } +}; + +//============================================================================= +// PencilTestPopup +//----------------------------------------------------------------------------- + +class PencilTestPopup : public DVGui::Dialog { + Q_OBJECT + + QCamera* m_currentCamera; + QString m_deviceName; + MyViewFinder* m_cameraViewfinder; + QCameraImageCapture* m_cameraImageCapture; + + QComboBox *m_cameraListCombo, *m_resolutionCombo, *m_fileTypeCombo, + *m_colorTypeCombo; + QLineEdit* m_levelNameEdit; + QCheckBox *m_upsideDownCB, *m_onionSkinCB; + QPushButton *m_fileFormatOptionButton, *m_captureWhiteBGButton; + DVGui::FileField* m_saveInFileFld; + DVGui::IntLineEdit* m_frameNumberEdit; + DVGui::IntField *m_thresholdFld, *m_contrastFld, *m_brightnessFld, + *m_bgReductionFld, *m_onionOpacityFld; + + QImage m_whiteBGImg; + + int m_timerId; + QString m_cacheImagePath; + bool m_captureWhiteBGCue; + bool m_captureCue; + + void processImage(QImage& procImage); + bool importImage(QImage& image); + +public: + PencilTestPopup(); + ~PencilTestPopup(); + +protected: + void timerEvent(QTimerEvent* event); + void showEvent(QShowEvent* event); + void hideEvent(QHideEvent* event); + +protected slots: + void refreshCameraList(); + void onCameraListComboActivated(int index); + void onResolutionComboActivated(const QString&); + void onFileFormatOptionButtonPressed(); + void onNextName(); + void onColorTypeComboChanged(int index); + void onImageCaptured(int, const QImage&); + void onCaptureWhiteBGButtonPressed(); + void onOnionCBToggled(bool); + void onOnionOpacityFldEdited(); + + void onCapture(); +}; + +#endif \ No newline at end of file diff --git a/toonz/sources/toonzqt/stylenameeditor.cpp b/toonz/sources/toonzqt/stylenameeditor.cpp index 57fa3db8..b325d9b4 100644 --- a/toonz/sources/toonzqt/stylenameeditor.cpp +++ b/toonz/sources/toonzqt/stylenameeditor.cpp @@ -27,7 +27,7 @@ StyleNameEditor::StyleNameEditor(QWidget *parent) m_applyButton->setEnabled(false); m_cancelButton->setFocusPolicy(Qt::NoFocus); - m_styleName->setObjectName("RenameColorTextField"); + m_styleName->setObjectName("LargeSizedText"); QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->setMargin(10); diff --git a/toonz/sources/translations/japanese/toonz.ts b/toonz/sources/translations/japanese/toonz.ts index 49bfaa6d..d20577db 100644 --- a/toonz/sources/translations/japanese/toonz.ts +++ b/toonz/sources/translations/japanese/toonz.ts @@ -4253,6 +4253,26 @@ Do you want to create it? Refresh Folder Tree フォルダ構成の再読み込み + + &Pencil Test... + ラインテスト (&P)... + + + Drawing Substitution Forward + + + + Drawing Substitution Backward + + + + Similar Drawing Substitution Forward + + + + Similar Drawing Substitution Backward + + MatchlinesDialog @@ -4503,6 +4523,13 @@ What do you want to do? ピクセル形式が対応されていません。 + + MyViewFinder + + Camera is not available + + + OutputSettingsPopup @@ -4880,6 +4907,145 @@ Do you want to overwrite it? 上書きしますか? + + PencilTestPopup + + Pencil Test + ラインテスト + + + Refresh + リスト更新 + + + File + ファイル + + + Options + ファイル設定 + + + Image adjust + 画像調整 + + + Upside down + 180度回転 + + + Capture white BG + 基準背景取り込み + + + Display + 表示 + + + Show onion skin + 前の画像と比べる + + + Capture + 取り込み + + + Close + 閉じる + + + Next Level + 次のレベル + + + Camera: + カメラ: + + + Resolution: + サイズ: + + + Name: + レベル名: + + + Frame: + フレーム: + + + File Type: + ファイルタイプ: + + + Save In: + 保存先: + + + Color type: + タイプ: + + + Threshold: + しきい値: + + + Contrast: + コントラスト: + + + Brightness: + 明るさ: + + + BG reduction: + 背景の除去: + + + Opacity(%): + 不透明度(%): + + + No camera found + カメラが見つかりません + + + No level name specified: please choose a valid level name + レベル名は指定されていません。有効なレベル名を指定して下さい。 + + + Folder %1 doesn't exist. +Do you want to create it? + フォルダー %1 は存在しません。 +作成しますか? + + + Unable to create + 作成できません。 + + + The level name specified is already used: please choose a different level name. + 指定されたレベル名はすでに使用中です:別の名前を指定してください + + + The save in path specified does not match with the existing level. + 指定された保存先パスが既存のレベルと異なります。 + + + The captured image size does not match with the existing level. + 取り込まれた画像サイズが既存のレベルと異なります。 + + + File %1 does exist. +Do you want to overwrite it? + ファイル %1 は、既に存在します。 +上書きしてもよろしいですか? + + + Failed to load %1. + ファイル %1 の読み込みに失敗しました + + PltGizmoPopup @@ -7070,6 +7236,10 @@ Are you sure to Add color model's palette to the destination palette. カラーモデルのパレットを、対象のパレットに追加する。 + + Change current drawing %1 + + RenameAsToonzPopup @@ -9408,6 +9578,10 @@ Please refer to the user guide for details. Double Click to Toggle Onion Skin [ダブルクリック] オニオンスキン表示/非表示 + + Pinned Center : Col%1%2 + + XsheetViewer