From 77b7d0abdabeea7e47c592e6c955bdf84d97a5b9 Mon Sep 17 00:00:00 2001 From: shun-iwasawa Date: Dec 18 2018 06:39:02 +0000 Subject: New Feature: Separate Colors (#2402) * separate colors * fix crash and set minimum size * modify output image * add show alpha toggle button --- diff --git a/toonz/sources/toonz/CMakeLists.txt b/toonz/sources/toonz/CMakeLists.txt index ca889ff..13eec76 100644 --- a/toonz/sources/toonz/CMakeLists.txt +++ b/toonz/sources/toonz/CMakeLists.txt @@ -159,6 +159,8 @@ set(MOC_HEADERS autoinputcellnumberpopup.h colormodelbehaviorpopup.h boardsettingspopup.h + separatecolorsswatch.h + separatecolorspopup.h # Tracker file dummyprocessor.h metnum.h @@ -333,6 +335,8 @@ set(SOURCES autoinputcellnumberpopup.cpp colormodelbehaviorpopup.cpp boardsettingspopup.cpp + separatecolorsswatch.cpp + separatecolorspopup.cpp # Tracker file dummyprocessor.cpp metnum.cpp diff --git a/toonz/sources/toonz/filebrowser.cpp b/toonz/sources/toonz/filebrowser.cpp index 30d4de0..a9ca57c 100644 --- a/toonz/sources/toonz/filebrowser.cpp +++ b/toonz/sources/toonz/filebrowser.cpp @@ -1199,6 +1199,8 @@ QMenu *FileBrowser::getContextMenu(QWidget *parent, int index) { // iwsw commented out temporarily // menu->addAction(cm->getAction(MI_ToonShadedImageToTLV)); } + if (areFullcolor) menu->addAction(cm->getAction(MI_SeparateColors)); + if (!areFullcolor) menu->addSeparator(); } if (files.size() == 1 && files[0].getType() != "tnz") { diff --git a/toonz/sources/toonz/fileselection.cpp b/toonz/sources/toonz/fileselection.cpp index c696e0c..e6a0563 100644 --- a/toonz/sources/toonz/fileselection.cpp +++ b/toonz/sources/toonz/fileselection.cpp @@ -11,6 +11,7 @@ #include "flipbook.h" #include "filebrowsermodel.h" #include "exportscenepopup.h" +#include "separatecolorspopup.h" #include "tapp.h" #include "batches.h" @@ -225,6 +226,7 @@ void FileSelection::enableCommands() { enableCommand(this, MI_ImportScenes, &FileSelection::importScenes); enableCommand(this, MI_ExportScenes, &FileSelection::exportScenes); enableCommand(this, MI_SelectAll, &FileSelection::selectAll); + enableCommand(this, MI_SeparateColors, &FileSelection::separateFilesByColors); } //------------------------------------------------------------------------ @@ -487,6 +489,25 @@ void FileSelection::collectAssets() { //------------------------------------------------------------------------ +void FileSelection::separateFilesByColors() { + std::vector files; + getSelectedFiles(files); + if (files.empty()) return; + + static SeparateColorsPopup *popup = new SeparateColorsPopup(); + if (popup->isConverting()) { + DVGui::MsgBox(INFORMATION, QObject::tr("A separation task is in progress! " + "wait until it stops or cancel it")); + return; + } + popup->setFiles(files); + popup->show(); + popup->raise(); + // popup->exec(); +} + +//------------------------------------------------------------------------ + static int importScene(TFilePath scenePath) { // bool dirtyFlag = TNotifier::instance()->getDirtyFlag(); ToonzScene scene; diff --git a/toonz/sources/toonz/fileselection.h b/toonz/sources/toonz/fileselection.h index d5a90b8..f3c76f8 100644 --- a/toonz/sources/toonz/fileselection.h +++ b/toonz/sources/toonz/fileselection.h @@ -44,6 +44,7 @@ public: void importScenes(); void exportScenes(); void selectAll(); + void separateFilesByColors(); }; #endif // FILESELECTION_H diff --git a/toonz/sources/toonz/mainwindow.cpp b/toonz/sources/toonz/mainwindow.cpp index aecd8af..b68138e 100644 --- a/toonz/sources/toonz/mainwindow.cpp +++ b/toonz/sources/toonz/mainwindow.cpp @@ -2046,6 +2046,8 @@ void MainWindow::defineActions() { createRightClickMenuAction(MI_DeactivateUpperColumns, tr("Hide Upper Columns"), ""); + createRightClickMenuAction(MI_SeparateColors, tr("Separate Colors..."), ""); + createToolAction(T_Edit, "edit", tr("Animate Tool"), "A"); createToolAction(T_Selection, "selection", tr("Selection Tool"), "S"); createToolAction(T_Brush, "brush", tr("Brush Tool"), "B"); diff --git a/toonz/sources/toonz/menubarcommandids.h b/toonz/sources/toonz/menubarcommandids.h index 09ec119..e27b12e 100644 --- a/toonz/sources/toonz/menubarcommandids.h +++ b/toonz/sources/toonz/menubarcommandids.h @@ -333,5 +333,6 @@ #define MI_LipSyncPopup "MI_LipSyncPopup" #define MI_AutoInputCellNumber "MI_AutoInputCellNumber" #define MI_TouchGestureControl "MI_TouchGestureControl" +#define MI_SeparateColors "MI_SeparateColors" #endif diff --git a/toonz/sources/toonz/separatecolorspopup.cpp b/toonz/sources/toonz/separatecolorspopup.cpp new file mode 100644 index 0000000..d8051e8 --- /dev/null +++ b/toonz/sources/toonz/separatecolorspopup.cpp @@ -0,0 +1,1748 @@ +#include "separatecolorspopup.h" + +#include "separatecolorsswatch.h" + +#include "menubarcommandids.h" +#include "tapp.h" +#include "tlevel_io.h" +#include "tiio.h" +#include "toutputproperties.h" +#include "filebrowser.h" + +#include "toonzqt/gutil.h" +#include "toonzqt/imageutils.h" +#include "toonzqt/menubarcommand.h" +#include "toonzqt/filefield.h" +#include "toonzqt/intfield.h" +#include "toonzqt/colorfield.h" +#include "toonzqt/checkbox.h" +#include "toonzqt/doublefield.h" +#include "toonz/tscenehandle.h" +#include "toonz/toonzscene.h" +#include "toonz/sceneproperties.h" +#include "tsystem.h" +#include "tenv.h" +#include "tpixelutils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TEnv::StringVar SeparateColorsPopup_PaperColor("SeparateColorsPopup_PaperColor", + "#FFFFFF"); +TEnv::StringVar SeparateColorsPopup_MainColor("SeparateColorsPopup_MainColor", + "#000000"); +TEnv::StringVar SeparateColorsPopup_SubColor1("SeparateColorsPopup_SubColor1", + "#FF0000"); +TEnv::StringVar SeparateColorsPopup_SubColor2("SeparateColorsPopup_SubColor2", + "#0000FF"); +TEnv::StringVar SeparateColorsPopup_SubColor3("SeparateColorsPopup_SubColor3", + "#00FF00"); + +TEnv::DoubleVar SeparateColorsPopup_SubAdjust("SeparateColorsPopup_SubAdjust", + 3.0); +TEnv::DoubleVar SeparateColorsPopup_BorderSmooth( + "SeparateColorsPopup_BorderSmooth", 0.1); +TEnv::IntVar SeparateColorsPopup_OutMain("SeparateColorsPopup_OutMain", 1); +TEnv::IntVar SeparateColorsPopup_OutSub1("SeparateColorsPopup_OutSub1", 1); +TEnv::IntVar SeparateColorsPopup_OutSub2("SeparateColorsPopup_OutSub2", 1); +TEnv::IntVar SeparateColorsPopup_OutSub3("SeparateColorsPopup_OutSub3", 0); +TEnv::StringVar SeparateColorsPopup_MainSuffix("SeparateColorsPopup_MainSuffix", + "_m"); +TEnv::StringVar SeparateColorsPopup_Sub1Suffix("SeparateColorsPopup_Sub1Suffix", + "_s1"); +TEnv::StringVar SeparateColorsPopup_Sub2Suffix("SeparateColorsPopup_Sub2Suffix", + "_s2"); +TEnv::StringVar SeparateColorsPopup_Sub3Suffix("SeparateColorsPopup_Sub3Suffix", + "_s3"); + +TEnv::IntVar SeparateColorsPopup_doMatte("SeparateColorsPopup_DoMatte", 1); +TEnv::DoubleVar SeparateColorsPopup_MatteThreshold( + "SeparateColorsPopup_MatteThreshold ", 0.0); +TEnv::IntVar SeparateColorsPopup_MatteRadius("SeparateColorsPopup_MatteRadius", + 0); + +TEnv::StringVar SeparateColorsPopup_FileFormat("SeparateColorsPopup_FileFormat", + "png"); + +//============================================================================= +// SeparateColorsPopup +//----------------------------------------------------------------------------- + +namespace { +//-------------------------------------------------------------------- +void getFrameIds(int from, int to, const TLevelP& level, + std::vector& frames) { + std::string msg; + int r0, r1; + TLevel::Iterator begin = level->begin(); + TLevel::Iterator end = level->end(); + end--; + + r0 = from; + r1 = to; + if (r0 > r1) { + int app = r0; + r0 = r1; + r1 = app; + } + TLevel::Iterator it = begin; + if (r0 != -1 && r0 <= level->getFrameCount()) std::advance(it, r0 - 1); + while (it != level->end() && r1 >= r0) { + frames.push_back(it->first); + if (r0 != -1) ++r0; + ++it; + } +} + +struct uchar3 { + unsigned char x, y, z; +}; +struct uchar4 { + unsigned char x, y, z, w; +}; + +// returns the factors of the equation of a plane which touches the specified 3 +// points +// a * x + b * y + c * z = d -> returns (a,b,c,d) +QVector4D getPane(QVector3D p0, QVector3D p1, QVector3D p2) { + // vector p0->p1 + QVector3D vec_p01 = p1 - p0; + // vector p0->p2 + QVector3D vec_p02 = p2 - p0; + + // cross product + QVector3D cross_p01_p02 = QVector3D::crossProduct(vec_p01, vec_p02); + + QVector4D pane(cross_p01_p02.x(), cross_p01_p02.y(), cross_p01_p02.z(), + cross_p01_p02.x() * p0.x() + cross_p01_p02.y() * p0.y() + + cross_p01_p02.z() * p0.z()); + return pane; +} + +QVector3D myPix2Rgb(TPixel32 pix) { + return QVector3D((float)pix.r / (float)TPixel32::maxChannelValue, + (float)pix.g / (float)TPixel32::maxChannelValue, + (float)pix.b / (float)TPixel32::maxChannelValue); +} + +QVector3D myRgb2Hls(QVector3D rgb) { + double h, l, s; + rgb2hls(rgb.x(), rgb.y(), rgb.z(), &h, &l, &s); + return QVector3D((float)h, (float)l, (float)s); +} + +QVector3D myHls2Xyz(QVector3D hls) { + float hueRad = hls.x() * 3.14159265f / 180.0f; + return QVector3D( + hls.z() * std::cos(hueRad) * (1.0f - (std::abs(hls.y() - 0.5f) * 2.0f)), + hls.y() - 0.5f, + hls.z() * std::sin(hueRad) * (1.0f - (std::abs(hls.y() - 0.5f) * 2.0f))); +} + +// modify values +float modify(float val) { + if (val >= 0.5f) return 1.0f; + + return val * 2.0f; +} + +// separate main color and two sub colors +QVector3D separate3_Kernel(QVector3D pix_xyz, QVector3D paper_xyz, + QVector3D main_xyz, QVector3D sub1_xyz, + QVector3D sub2_xyz, QVector4D pane, float mainAdjust, + float borderRatio) { + // vector paper -> pixel + QVector3D vec_e = pix_xyz - paper_xyz; + + float de = pane.x() * vec_e.x() + pane.y() * vec_e.y() + pane.z() * vec_e.z(); + + // if de == 0, vec_e is in pallarel with the pane + if (de == 0.0f) return QVector3D(0.0f, 0.0f, 0.0f); + + // compute the factor t which extends vec_e to the pane + float t = (pane.w() - (pane.x() * paper_xyz.x() + pane.y() * paper_xyz.y() + + pane.z() * paper_xyz.z())) / + de; + + // intersecting point on the pane + QVector3D Q = paper_xyz + t * vec_e; + + // ratio_ms1 + // vector main color -> Q + QVector3D vec_mQ = Q - main_xyz; + // vector main color -> sub1 color + QVector3D vec_ms1 = sub1_xyz - main_xyz; + float ratio_ms1 = + QVector3D::dotProduct(vec_mQ, vec_ms1) / vec_ms1.lengthSquared(); + + // value_ms1 + float value_ms1; + if (ratio_ms1 <= 1.0f / (1.0f + mainAdjust) - borderRatio) + value_ms1 = 1.0f; + else if (ratio_ms1 < 1.0f / (1.0f + mainAdjust) + borderRatio) + value_ms1 = 1.0f - + (ratio_ms1 - 1.0f / (1.0f + mainAdjust) + borderRatio) / + (2.0f * borderRatio); + else + value_ms1 = 0.0f; + + // ratio_ms2 + // vector main color -> sub2 color + QVector3D vec_ms2 = sub2_xyz - main_xyz; + float ratio_ms2 = + QVector3D::dotProduct(vec_mQ, vec_ms2) / vec_ms2.lengthSquared(); + + // value_ms2 + float value_ms2; + if (ratio_ms2 <= 1.0f / (1.0f + mainAdjust) - borderRatio) + value_ms2 = 1.0f; + else if (ratio_ms2 < 1.0f / (1.0f + mainAdjust) + borderRatio) + value_ms2 = 1.0f - + (ratio_ms2 - 1.0f / (1.0f + mainAdjust) + borderRatio) / + (2.0f * borderRatio); + else + value_ms2 = 0.0f; + + // ratio_s1s2 + // vector sub1 color -> Q + QVector3D vec_s1Q = Q - sub1_xyz; + // vector sub1 color -> sub2 color + QVector3D vec_s1s2 = sub2_xyz - sub1_xyz; + float ratio_s1s2 = + QVector3D::dotProduct(vec_s1Q, vec_s1s2) / vec_s1s2.lengthSquared(); + + // value_s1s2 + float value_s1s2; + if (ratio_s1s2 <= 0.5f - borderRatio) + value_s1s2 = 1.0f; + else if (ratio_s1s2 < 0.5f + borderRatio) + value_s1s2 = + 1.0f - (ratio_s1s2 - 0.5f + borderRatio) / (2.0f * borderRatio); + else + value_s1s2 = 0.0f; + + float base_m = modify(value_ms1) * modify(value_ms2); + float base_s1 = modify(1.0f - value_ms1) * modify(value_s1s2); + float base_s2 = modify(1.0f - value_ms2) * modify(1.0f - value_s1s2); + + // vector paper color -> main color + QVector3D vec_pm = main_xyz - paper_xyz; + // vector paper color -> sub1 color + QVector3D vec_ps1 = sub1_xyz - paper_xyz; + // vector paper color -> sub2 color + QVector3D vec_ps2 = sub2_xyz - paper_xyz; + + float ratio_m = QVector3D::dotProduct(vec_e, vec_pm) / vec_pm.lengthSquared(); + float ratio_s1 = + QVector3D::dotProduct(vec_e, vec_ps1) / vec_ps1.lengthSquared(); + float ratio_s2 = + QVector3D::dotProduct(vec_e, vec_ps2) / vec_ps2.lengthSquared(); + + return QVector3D(std::max(0.0f, base_m * ratio_m), + std::max(0.0f, base_s1 * ratio_s1), + std::max(0.0f, base_s2 * ratio_s2)); +} + +// compute intensity of each pencil color +// main = out.x(), sub1 = out.y(), sub2 = out.z() +void separate3Colors(int i, QVector3D* src, QVector3D* out, QVector3D paper_xyz, + QVector3D main_xyz, QVector3D sub1_xyz, QVector3D sub2_xyz, + QVector4D pane, float mainAdjust, float borderRatio) { + // compute HLS value of the target pixel + // then convert HLS value to XYZ coordinate + QVector3D pix_xyz = myHls2Xyz(myRgb2Hls(src[i])); + + // pixels lighter than the paper color is 100% paper + if (pix_xyz.y() >= paper_xyz.y()) { + out[i].setX(0.0f); + out[i].setY(0.0f); + out[i].setZ(0.0f); + return; + } + + out[i] = separate3_Kernel(pix_xyz, paper_xyz, main_xyz, sub1_xyz, sub2_xyz, + pane, mainAdjust, borderRatio); +} + +QVector2D getFactor(QVector3D vec, QVector3D point1, QVector3D point2) { + float b = (point1.x() * vec.y() - point1.y() * vec.x()) / + (point1.x() * point2.y() - point2.x() * point1.y()); + float a = (vec.x() - b * point2.x()) / point1.x(); + + return QVector2D(a, b); +} + +// compute intensity of each pencil color +// main = out.x(), sub1 = out.y(), sub2 = out.z(), sub3 = out.w() +void separate4Colors(int i, QVector3D* src, QVector4D* out, QVector3D paper_xyz, + QVector3D main_xyz, QVector3D sub1_xyz, QVector3D sub2_xyz, + QVector3D sub3_xyz, QVector4D pane_m12, QVector4D pane_m13, + QVector4D pane_m23, QVector4D pane_123, + QVector3D R, // intersection of paper->main vector and + // plane of 3 sub colors + float mainAdjust, float borderRatio) { + // compute HLS value of the target pixel + // then convert HLS value to XYZ coordinate + QVector3D pix_xyz = myHls2Xyz(myRgb2Hls(src[i])); + + // pixels lighter than the paper color is 100% paper + if (pix_xyz.y() >= paper_xyz.y()) { + out[i].setX(0.0f); + out[i].setY(0.0f); + out[i].setZ(0.0f); + out[i].setW(0.0f); + return; + } + + // vector paper -> pixel + QVector3D vec_e = pix_xyz - paper_xyz; + + float de = pane_123.x() * vec_e.x() + pane_123.y() * vec_e.y() + + pane_123.z() * vec_e.z(); + + // if de == 0, vec_e is in pallarel with the pane + if (de == 0.0f) { + out[i].setX(0.0f); + out[i].setY(0.0f); + out[i].setZ(0.0f); + out[i].setW(0.0f); + return; + } + + // compute the factor t which extends vec_e to the pane + float t = (pane_123.w() - + (pane_123.x() * paper_xyz.x() + pane_123.y() * paper_xyz.y() + + pane_123.z() * paper_xyz.z())) / + de; + + // intersecting point on the pane + QVector3D Q = paper_xyz + t * vec_e; + + // vector R (pencil on the pane) -> Q (current pixel on the pane) + QVector3D vec_RQ = Q - R; + + // vector from R to each sub color + QVector3D vec_RS1 = sub1_xyz - R; + QVector3D vec_RS2 = sub2_xyz - R; + QVector3D vec_RS3 = sub3_xyz - R; + + // define which two sub colors are mixed in this pixel. + // obtain factors + QVector2D k_S1S2 = getFactor(vec_RQ, vec_RS1, vec_RS2); + + // in case of sub1 and sub2. sub3 is 0 + if (k_S1S2.x() >= 0.0f && k_S1S2.y() >= 0.0f) { + QVector3D ret = + separate3_Kernel(pix_xyz, paper_xyz, main_xyz, sub1_xyz, sub2_xyz, + pane_m12, mainAdjust, borderRatio); + + out[i].setX(ret.x()); + out[i].setY(ret.y()); + out[i].setZ(ret.z()); + out[i].setW(0.0f); + } else { + QVector2D k_S1S3 = getFactor(vec_RQ, vec_RS1, vec_RS3); + // in case of sub1 and sub3. sub2 is 0 + if (k_S1S3.x() >= 0.0f && k_S1S3.y() >= 0.0f) { + QVector3D ret = + separate3_Kernel(pix_xyz, paper_xyz, main_xyz, sub1_xyz, sub3_xyz, + pane_m13, mainAdjust, borderRatio); + + out[i].setX(ret.x()); + out[i].setY(ret.y()); + out[i].setZ(0.0f); + out[i].setW(ret.z()); + } + // in case of sub2 and sub3. sub1 is 0 + else { + QVector3D ret = + separate3_Kernel(pix_xyz, paper_xyz, main_xyz, sub2_xyz, sub3_xyz, + pane_m23, mainAdjust, borderRatio); + + out[i].setX(ret.x()); + out[i].setY(0.0f); + out[i].setZ(ret.y()); + out[i].setW(ret.z()); + } + } +} + +void maskByThres(int i, QVector3D* out, uchar3* mask, float maskThres) { + if (out[i].x() < maskThres) + mask[i].x = 0; + else + mask[i].x = 1; + + if (out[i].y() < maskThres) + mask[i].y = 0; + else + mask[i].y = 1; + + if (out[i].z() < maskThres) + mask[i].z = 0; + else + mask[i].z = 1; +} + +void maskByThres(int i, QVector4D* out, uchar4* mask, float maskThres) { + if (out[i].x() < maskThres) + mask[i].x = 0; + else + mask[i].x = 1; + + if (out[i].y() < maskThres) + mask[i].y = 0; + else + mask[i].y = 1; + + if (out[i].z() < maskThres) + mask[i].z = 0; + else + mask[i].z = 1; + + if (out[i].w() < maskThres) + mask[i].w = 0; + else + mask[i].w = 1; +} + +void expandMask(int x, int y, uchar3* mask_host, int lx, int ly, int gen) { + int i = y * lx + x; + // check if the target pixel is already opac + bool foundMain = (mask_host[i].x >= 1 && mask_host[i].x <= gen + 1); + bool foundSub1 = (mask_host[i].y >= 1 && mask_host[i].y <= gen + 1); + bool foundSub2 = (mask_host[i].z >= 1 && mask_host[i].z <= gen + 1); + + if (foundMain && foundSub1 && foundSub2) return; + + // search neighbor pixels' mask_host values + for (int yy = y - 1; yy <= y + 1; yy++) { + // boundary condition + if (yy < 0 || yy >= ly) continue; + + // current searching index + int curIndex = yy * lx + x - 1; + + for (int xx = x - 1; xx <= x + 1; xx++, curIndex++) { + // boundary condition + if (xx < 0 || xx >= lx) continue; + + // update the flags if the searching pixel is opac + if (!foundMain && mask_host[curIndex].x >= 1 && + mask_host[curIndex].x <= gen + 1) + foundMain = true; + if (!foundSub1 && mask_host[curIndex].y >= 1 && + mask_host[curIndex].y <= gen + 1) + foundSub1 = true; + if (!foundSub2 && mask_host[curIndex].z >= 1 && + mask_host[curIndex].z <= gen + 1) + foundSub2 = true; + + // end the loop when all colors are found + if (foundMain && foundSub1 && foundSub2) break; + } + // end the loop when all colors are found + if (foundMain && foundSub1 && foundSub2) break; + } + + // update mask_host + if (foundMain && mask_host[i].x == 0) mask_host[i].x = gen + 2; + if (foundSub1 && mask_host[i].y == 0) mask_host[i].y = gen + 2; + if (foundSub2 && mask_host[i].z == 0) mask_host[i].z = gen + 2; +} + +void expandMask(int x, int y, uchar4* mask_host, int lx, int ly, int gen) { + int i = y * lx + x; + // check if the target pixel is already opac + bool foundMain = (mask_host[i].x >= 1 && mask_host[i].x <= gen + 1); + bool foundSub1 = (mask_host[i].y >= 1 && mask_host[i].y <= gen + 1); + bool foundSub2 = (mask_host[i].z >= 1 && mask_host[i].z <= gen + 1); + bool foundSub3 = (mask_host[i].w >= 1 && mask_host[i].w <= gen + 1); + + if (foundMain && foundSub1 && foundSub2 && foundSub3) return; + + // search neighbor pixels' mask_host values + for (int yy = y - 1; yy <= y + 1; yy++) { + // boundary condition + if (yy < 0 || yy >= ly) continue; + + // current searching index + int curIndex = yy * lx + x - 1; + + for (int xx = x - 1; xx <= x + 1; xx++, curIndex++) { + // boundary condition + if (xx < 0 || xx >= lx) continue; + + // update the flags if the searching pixel is opac + if (!foundMain && mask_host[curIndex].x >= 1 && + mask_host[curIndex].x <= gen + 1) + foundMain = true; + if (!foundSub1 && mask_host[curIndex].y >= 1 && + mask_host[curIndex].y <= gen + 1) + foundSub1 = true; + if (!foundSub2 && mask_host[curIndex].z >= 1 && + mask_host[curIndex].z <= gen + 1) + foundSub2 = true; + if (!foundSub3 && mask_host[curIndex].w >= 1 && + mask_host[curIndex].w <= gen + 1) + foundSub3 = true; + + // end the loop when all colors are found + if (foundMain && foundSub1 && foundSub2 && foundSub3) break; + } + // end the loop when all colors are found + if (foundMain && foundSub1 && foundSub2 && foundSub3) break; + } + + // update mask_host + if (foundMain && mask_host[i].x == 0) mask_host[i].x = gen + 2; + if (foundSub1 && mask_host[i].y == 0) mask_host[i].y = gen + 2; + if (foundSub2 && mask_host[i].z == 0) mask_host[i].z = gen + 2; + if (foundSub3 && mask_host[i].w == 0) mask_host[i].w = gen + 2; +} + +TPixel32 BlueMaskColor(128, 128, 255); +TPixel32 RedMaskColor(255, 128, 128); +}; + +//------------------------------------------------------------------- + +void Separate3ColorsTask::run() { + for (int i = m_from; i < m_to; i++) { + separate3Colors(i, m_src, m_out, m_paper_xyz, m_main_xyz, m_sub1_xyz, + m_sub2_xyz, m_pane, m_mainAdjust, m_borderRatio); + } +} + +//------------------------------------------------------------------- + +void Separate4ColorsTask::run() { + for (int i = m_from; i < m_to; i++) { + separate4Colors(i, m_src, m_out, m_paper_xyz, m_main_xyz, m_sub1_xyz, + m_sub2_xyz, m_sub3_xyz, m_pane_m12, m_pane_m13, m_pane_m23, + m_pane_123, m_R, m_mainAdjust, m_borderRatio); + } +} + +//------------------------------------------------------------------- + +SeparateColorsPopup::SeparateColorsPopup() + : DVGui::Dialog(TApp::instance()->getMainWindow(), false, false, + "SeparateColors") + , m_isConverting(false) { + setModal(false); + setMinimumSize(900, 670); + + QSplitter* mainSplitter = new QSplitter(this); + + m_autoBtn = new QPushButton(tr("Auto"), this); + m_previewBtn = new QPushButton(tr("Preview"), this); + m_okBtn = new QPushButton(tr("Separate"), this); + m_cancelBtn = new QPushButton(tr("Close"), this); + m_fromFld = new IntLineEdit(this); + m_toFld = new IntLineEdit(this); + m_saveInFileFld = new FileField(0, QString("")); + m_fileFormatCombo = new QComboBox(this); + + m_paperColorField = + new DVGui::ColorField(this, false, TPixel32(249, 246, 243), 35, true, 55); + m_mainColorField = + new DVGui::ColorField(this, false, TPixel32(176, 171, 169), 35, true, 55); + m_subColor1Field = + new DVGui::ColorField(this, false, TPixel32(249, 164, 164), 35, true, 55); + m_subColor2Field = + new DVGui::ColorField(this, false, TPixel32(165, 227, 187), 35, true, 55); + + QLabel* color3Label = new QLabel(tr("Sub Color 3:"), this); + m_subColor3Field = + new DVGui::ColorField(this, false, TPixel32::Green, 35, true, 55); + + m_subColorAdjustFld = new DoubleField(this); + m_borderSmoothnessFld = new DoubleField(this); + + m_matteGroupBox = new QGroupBox(tr("Alpha Matting"), this); + m_matteThreshold = new DoubleField(this); + m_matteRadius = new IntField(this); + + m_notifier = new ImageUtils::FrameTaskNotifier(); + + m_outMainCB = new QCheckBox(tr("Main"), this); + m_outSub1CB = new QCheckBox(tr("Sub1"), this); + m_outSub2CB = new QCheckBox(tr("Sub2"), this); + m_outSub3CB = new QCheckBox(tr("Sub3"), this); + + m_mainSuffixEdit = new LineEdit(QString("_m")); + m_sub1SuffixEdit = new LineEdit(QString("_s1")); + m_sub2SuffixEdit = new LineEdit(QString("_s2")); + m_sub3SuffixEdit = new LineEdit(QString("_s3")); + + m_previewFrameField = new IntField(this); + m_previewFrameLabel = new QLabel(this); + + m_pickBtn = new QPushButton( + CommandManager::instance()->getAction("T_RGBPicker")->icon(), + tr("Pick Color"), this); + + QPixmap iconPm(13, 13); + iconPm.fill( + QColor((int)RedMaskColor.r, (int)RedMaskColor.g, (int)RedMaskColor.b)); + m_showMatteBtn = new QPushButton(QIcon(iconPm), tr("Show Mask"), this); + + { + iconPm.fill(Qt::black); + QPainter icon_p(&iconPm); + icon_p.setPen(Qt::NoPen); + icon_p.setBrush(Qt::white); + icon_p.drawEllipse(3, 3, 7, 7); + m_showAlphaBtn = new QPushButton(QIcon(iconPm), tr("Show Alpha"), this); + } + + m_separateSwatch = new SeparateSwatch(this, 200, 150); + + //---- + + m_autoBtn->setStyleSheet("font-size: 17px;"); + m_autoBtn->setFixedHeight(60); + m_autoBtn->setCheckable(true); + m_autoBtn->setChecked(true); + + m_previewBtn->setStyleSheet("font-size: 17px;"); + m_previewBtn->setFixedHeight(60); + m_previewBtn->setDisabled(true); + + m_okBtn->setStyleSheet("font-size: 17px;"); + m_okBtn->setFixedHeight(60); + + m_fileFormatCombo->addItem("png"); + m_fileFormatCombo->addItem("tif"); + + m_subColorAdjustFld->setRange(0.5, 10.0); + m_borderSmoothnessFld->setRange(0.0, 0.5); + m_subColorAdjustFld->setValue(3.0); + m_borderSmoothnessFld->setValue(0.1); + + m_matteGroupBox->setCheckable(true); + m_matteGroupBox->setChecked(true); + m_matteThreshold->setRange(0.0, 1.0); + m_matteRadius->setRange(0, 30); + m_matteThreshold->setValue(0.0); + m_matteRadius->setValue(0); + + m_outMainCB->setChecked(true); + m_outSub1CB->setChecked(true); + m_outSub2CB->setChecked(true); + + m_outSub3CB->setChecked(false); + m_sub3SuffixEdit->setEnabled(false); + color3Label->setVisible(false); + m_subColor3Field->setVisible(false); + + m_pickBtn->setMaximumWidth(130); + + m_showMatteBtn->setCheckable(true); + m_showMatteBtn->setChecked(true); + + m_showAlphaBtn->setCheckable(true); + m_showAlphaBtn->setChecked(false); + + //---- + { + QVBoxLayout* leftLay = new QVBoxLayout(); + leftLay->setMargin(5); + leftLay->setSpacing(5); + { + QHBoxLayout* previewLay = new QHBoxLayout(); + previewLay->setMargin(0); + previewLay->setSpacing(5); + { + previewLay->addWidget(new QLabel(tr("Preview Frame:"), this), 0, + Qt::AlignRight); + previewLay->addWidget(m_previewFrameField, 1); + previewLay->addWidget(m_previewFrameLabel, 0); + + previewLay->addSpacing(10); + + previewLay->addWidget(m_showMatteBtn, 0); + previewLay->addWidget(m_showAlphaBtn, 0); + + previewLay->addSpacing(20); + + previewLay->addWidget(m_pickBtn); + } + leftLay->addLayout(previewLay, 0); + + leftLay->addWidget(m_separateSwatch, 1); + } + QWidget* leftWidget = new QWidget(this); + leftWidget->setLayout(leftLay); + mainSplitter->addWidget(leftWidget); + + QVBoxLayout* rightLay = new QVBoxLayout(); + rightLay->setMargin(10); + rightLay->setSpacing(10); + { + QGridLayout* upperLay = new QGridLayout(); + upperLay->setMargin(0); + upperLay->setHorizontalSpacing(5); + upperLay->setVerticalSpacing(10); + { + upperLay->addWidget(new QLabel(tr("Paper Color:"), this), 0, 0, + Qt::AlignRight); + upperLay->addWidget(m_paperColorField, 0, 1); + upperLay->addWidget(new QLabel(tr("Main Color:"), this), 1, 0, + Qt::AlignRight); + upperLay->addWidget(m_mainColorField, 1, 1); + upperLay->addWidget(new QLabel(tr("Sub Color 1:"), this), 2, 0, + Qt::AlignRight); + upperLay->addWidget(m_subColor1Field, 2, 1); + upperLay->addWidget(new QLabel(tr("Sub Color 2:"), this), 3, 0, + Qt::AlignRight); + upperLay->addWidget(m_subColor2Field, 3, 1); + + upperLay->addWidget(color3Label, 4, 0, Qt::AlignRight); + upperLay->addWidget(m_subColor3Field, 4, 1); + + upperLay->addWidget(new QLabel(tr("Sub Adjust:"), this), 5, 0, + Qt::AlignRight); + upperLay->addWidget(m_subColorAdjustFld, 5, 1); + upperLay->addWidget(new QLabel(tr("Border Smooth:"), this), 6, 0, + Qt::AlignRight); + upperLay->addWidget(m_borderSmoothnessFld, 6, 1); + + QGridLayout* maskLay = new QGridLayout(); + maskLay->setMargin(10); + maskLay->setHorizontalSpacing(5); + maskLay->setVerticalSpacing(10); + { + maskLay->addWidget(new QLabel(tr("Mask Threshold:"), this), 0, 0, + Qt::AlignRight); + maskLay->addWidget(m_matteThreshold, 0, 1); + maskLay->addWidget(new QLabel(tr("Mask Radius:"), this), 1, 0, + Qt::AlignRight); + maskLay->addWidget(m_matteRadius, 1, 1); + } + maskLay->setColumnStretch(0, 0); + maskLay->setColumnStretch(1, 1); + m_matteGroupBox->setLayout(maskLay); + upperLay->addWidget(m_matteGroupBox, 7, 0, 1, 2); + } + upperLay->setColumnStretch(0, 0); + upperLay->setColumnStretch(1, 1); + + rightLay->addLayout(upperLay, 0); + + QGridLayout* middleLay = new QGridLayout(); + middleLay->setMargin(0); + middleLay->setHorizontalSpacing(5); + middleLay->setVerticalSpacing(10); + { + middleLay->addWidget(new QLabel(tr("Start:"), this), 0, 0, + Qt::AlignRight); + QHBoxLayout* rangeLay = new QHBoxLayout(); + rangeLay->setMargin(0); + rangeLay->setSpacing(5); + { + rangeLay->addWidget(m_fromFld, 0); + rangeLay->addSpacing(10); + rangeLay->addWidget(new QLabel(tr("End:"), this), 0); + rangeLay->addWidget(m_toFld, 0); + rangeLay->addStretch(1); + rangeLay->addWidget(new QLabel(tr("Format:"), this), 0); + rangeLay->addWidget(m_fileFormatCombo, 0); + } + middleLay->addLayout(rangeLay, 0, 1, 1, 6); + + middleLay->addWidget(new QLabel(tr("Save in:"), this), 1, 0, + Qt::AlignRight); + middleLay->addWidget(m_saveInFileFld, 1, 1, 1, 6); + + middleLay->addWidget(new QLabel(tr("File Suffix:"), this), 2, 0, + Qt::AlignRight); + middleLay->addWidget(m_outMainCB, 2, 1, + Qt::AlignRight | Qt::AlignVCenter); + middleLay->addWidget(m_mainSuffixEdit, 2, 2); + middleLay->addWidget(m_outSub1CB, 2, 4, + Qt::AlignRight | Qt::AlignVCenter); + middleLay->addWidget(m_sub1SuffixEdit, 2, 5); + + middleLay->addWidget(m_outSub2CB, 3, 1, + Qt::AlignRight | Qt::AlignVCenter); + middleLay->addWidget(m_sub2SuffixEdit, 3, 2); + middleLay->addWidget(m_outSub3CB, 3, 4, + Qt::AlignRight | Qt::AlignVCenter); + middleLay->addWidget(m_sub3SuffixEdit, 3, 5); + } + middleLay->setColumnStretch(0, 0); + middleLay->setColumnStretch(1, 0); + middleLay->setColumnStretch(2, 0); + middleLay->setColumnStretch(3, 0); + middleLay->setColumnStretch(4, 0); + middleLay->setColumnStretch(5, 0); + middleLay->setColumnStretch(6, 1); + rightLay->addLayout(middleLay, 0); + + rightLay->addSpacing(10); + + QHBoxLayout* buttonLay = new QHBoxLayout(); + buttonLay->setMargin(0); + buttonLay->setSpacing(0); + { + buttonLay->addWidget(m_autoBtn, 0); + buttonLay->addWidget(m_previewBtn, 1); + buttonLay->addSpacing(30); + buttonLay->addWidget(m_okBtn, 1); + } + rightLay->addLayout(buttonLay, 0); + rightLay->addSpacing(10); + rightLay->addWidget(m_cancelBtn, 0); + + rightLay->addStretch(1); + } + QWidget* widget = new QWidget(this); + widget->setLayout(rightLay); + + mainSplitter->addWidget(widget); + } + mainSplitter->setStretchFactor(0, 1); + mainSplitter->setStretchFactor(1, 0); + m_topLayout->addWidget(mainSplitter, 1); + + //---- + + bool ret = true; + + ret = ret && connect(m_autoBtn, SIGNAL(toggled(bool)), m_previewBtn, + SLOT(setDisabled(bool))); + ret = ret && connect(m_previewBtn, SIGNAL(clicked()), this, + SLOT(onPreviewBtnPressed())); + ret = ret && connect(m_okBtn, SIGNAL(clicked()), this, SLOT(separate())); + ret = ret && connect(m_cancelBtn, SIGNAL(clicked()), this, SLOT(reject())); + + m_progressDialog = new DVGui::ProgressDialog("", tr("Cancel"), 0, 0); + m_progressDialog->setWindowTitle(tr("Separate by colors ... ")); + m_progressDialog->setWindowFlags( + Qt::Dialog | Qt::WindowTitleHint); // Don't show ? and X buttons + // m_progressDialog->setWindowModality(Qt::WindowModal); + m_progressDialog->setMinimum(0); + m_progressDialog->setMaximum(100); + ret = ret && connect(m_notifier, SIGNAL(frameCompleted(int)), + m_progressDialog, SLOT(setValue(int))); + ret = ret && connect(m_progressDialog, SIGNAL(canceled()), m_notifier, + SLOT(onCancelTask())); + + ret = ret && connect(m_outMainCB, SIGNAL(toggled(bool)), m_mainSuffixEdit, + SLOT(setEnabled(bool))); + ret = ret && connect(m_outSub1CB, SIGNAL(toggled(bool)), m_sub1SuffixEdit, + SLOT(setEnabled(bool))); + ret = ret && connect(m_outSub2CB, SIGNAL(toggled(bool)), m_sub2SuffixEdit, + SLOT(setEnabled(bool))); + + ret = ret && connect(m_outSub3CB, SIGNAL(toggled(bool)), color3Label, + SLOT(setVisible(bool))); + ret = ret && connect(m_outSub3CB, SIGNAL(toggled(bool)), m_subColor3Field, + SLOT(setVisible(bool))); + ret = ret && connect(m_outSub3CB, SIGNAL(toggled(bool)), m_sub3SuffixEdit, + SLOT(setEnabled(bool))); + ret = ret && connect(m_outSub3CB, SIGNAL(toggled(bool)), m_separateSwatch, + SLOT(showSub3Swatch(bool))); + + ret = ret && connect(m_previewFrameField, SIGNAL(valueChanged(bool)), this, + SLOT(onPreviewFrameFieldValueChanged(bool))); + + QAction* pickScreenAction = + CommandManager::instance()->getAction("A_ToolOption_PickScreen"); + ret = ret && connect(m_pickBtn, SIGNAL(clicked()), pickScreenAction, + SLOT(trigger())); + + ret = ret && connect(m_matteGroupBox, SIGNAL(toggled(bool)), m_showMatteBtn, + SLOT(setEnabled(bool))); + + // auto preview update + ret = + ret && connect(m_autoBtn, SIGNAL(toggled(bool)), this, SLOT(onToggle())); + ret = ret && connect(m_subColorAdjustFld, SIGNAL(valueChanged(bool)), this, + SLOT(onChange(bool))); + ret = ret && connect(m_borderSmoothnessFld, SIGNAL(valueChanged(bool)), this, + SLOT(onChange(bool))); + ret = ret && + connect(m_matteGroupBox, SIGNAL(toggled(bool)), this, SLOT(onToggle())); + ret = ret && connect(m_matteThreshold, SIGNAL(valueChanged(bool)), this, + SLOT(onChange(bool))); + ret = ret && connect(m_matteRadius, SIGNAL(valueChanged(bool)), this, + SLOT(onChange(bool))); + ret = ret && + connect(m_outSub3CB, SIGNAL(toggled(bool)), this, SLOT(onToggle())); + ret = ret && + connect(m_paperColorField, SIGNAL(colorChanged(const TPixel32&, bool)), + this, SLOT(onColorChange(const TPixel32&, bool))); + ret = ret && + connect(m_mainColorField, SIGNAL(colorChanged(const TPixel32&, bool)), + this, SLOT(onColorChange(const TPixel32&, bool))); + ret = ret && + connect(m_subColor1Field, SIGNAL(colorChanged(const TPixel32&, bool)), + this, SLOT(onColorChange(const TPixel32&, bool))); + ret = ret && + connect(m_subColor2Field, SIGNAL(colorChanged(const TPixel32&, bool)), + this, SLOT(onColorChange(const TPixel32&, bool))); + ret = ret && + connect(m_subColor3Field, SIGNAL(colorChanged(const TPixel32&, bool)), + this, SLOT(onColorChange(const TPixel32&, bool))); + ret = ret && + connect(m_showMatteBtn, SIGNAL(toggled(bool)), this, SLOT(onToggle())); + ret = ret && + connect(m_showAlphaBtn, SIGNAL(toggled(bool)), this, SLOT(onToggle())); + + assert(ret); + + //----- reproduce UI + + QColor paperCol, mainCol, sub1Col, sub2Col, sub3Col; + paperCol.setNamedColor( + QString::fromStdString(SeparateColorsPopup_PaperColor.getValue())); + mainCol.setNamedColor( + QString::fromStdString(SeparateColorsPopup_MainColor.getValue())); + sub1Col.setNamedColor( + QString::fromStdString(SeparateColorsPopup_SubColor1.getValue())); + sub2Col.setNamedColor( + QString::fromStdString(SeparateColorsPopup_SubColor2.getValue())); + sub3Col.setNamedColor( + QString::fromStdString(SeparateColorsPopup_SubColor3.getValue())); + + m_paperColorField->setColor( + TPixel32(paperCol.red(), paperCol.green(), paperCol.blue())); + m_mainColorField->setColor( + TPixel32(mainCol.red(), mainCol.green(), mainCol.blue())); + m_subColor1Field->setColor( + TPixel32(sub1Col.red(), sub1Col.green(), sub1Col.blue())); + m_subColor2Field->setColor( + TPixel32(sub2Col.red(), sub2Col.green(), sub2Col.blue())); + m_subColor3Field->setColor( + TPixel32(sub3Col.red(), sub3Col.green(), sub3Col.blue())); + + m_subColorAdjustFld->setValue(SeparateColorsPopup_SubAdjust); + m_borderSmoothnessFld->setValue(SeparateColorsPopup_BorderSmooth); + + m_outMainCB->setChecked(SeparateColorsPopup_OutMain ? 1 : 0); + m_outSub1CB->setChecked(SeparateColorsPopup_OutSub1 ? 1 : 0); + m_outSub2CB->setChecked(SeparateColorsPopup_OutSub2 ? 1 : 0); + m_outSub3CB->setChecked(SeparateColorsPopup_OutSub3 ? 1 : 0); + + m_mainSuffixEdit->setText( + QString::fromStdString(SeparateColorsPopup_MainSuffix.getValue())); + m_sub1SuffixEdit->setText( + QString::fromStdString(SeparateColorsPopup_Sub1Suffix.getValue())); + m_sub2SuffixEdit->setText( + QString::fromStdString(SeparateColorsPopup_Sub2Suffix.getValue())); + m_sub3SuffixEdit->setText( + QString::fromStdString(SeparateColorsPopup_Sub3Suffix.getValue())); + + m_matteGroupBox->setChecked(SeparateColorsPopup_doMatte == 1 ? true : false); + m_matteThreshold->setValue(SeparateColorsPopup_MatteThreshold); + m_matteRadius->setValue(SeparateColorsPopup_MatteRadius); + + m_fileFormatCombo->setCurrentText( + QString::fromStdString(SeparateColorsPopup_FileFormat)); +} + +//------------------------------------------------------------------- + +void SeparateColorsPopup::onSeparateFinished() { + m_isConverting = false; + TFilePath dstFilePath(m_saveInFileFld->getPath().toStdWString()); + if (dstFilePath == m_srcFilePaths[0].getParentDir()) + FileBrowser::refreshFolder(dstFilePath); + + m_progressDialog->close(); +} + +//------------------------------------------------------------------- + +SeparateColorsPopup::~SeparateColorsPopup() { + delete m_notifier; + delete m_progressDialog; +} + +//------------------------------------------------------------------- + +bool SeparateColorsPopup::isConverting() { return m_isConverting; } + +//------------------------------------------------------------------- + +void SeparateColorsPopup::setFiles(const std::vector& fps) { + if (!m_srcFrames.isEmpty()) m_srcFrames.clear(); + + m_srcFilePaths = fps; + if (m_srcFilePaths.size() == 1) { + setWindowTitle(tr("Separate 1 Level")); + m_fromFld->setEnabled(true); + m_toFld->setEnabled(true); + + TLevelP levelTmp; + TLevelReaderP lrTmp = TLevelReaderP(fps[0]); + if (lrTmp) levelTmp = lrTmp->loadInfo(); + m_fromFld->setText("1"); + m_toFld->setText(QString::number(levelTmp->getFrameCount())); + + std::vector frames; + getFrameIds(1, levelTmp->getFrameCount(), levelTmp, frames); + for (int f = 0; f < frames.size(); f++) + m_srcFrames.append(qMakePair(fps[0], frames[f])); + } else { + setWindowTitle(tr("Separate %1 Levels").arg(m_srcFilePaths.size())); + m_fromFld->setText(""); + m_toFld->setText(""); + m_fromFld->setEnabled(false); + m_toFld->setEnabled(false); + for (int s = 0; s < fps.size(); s++) { + TLevelReaderP lrTmp = TLevelReaderP(fps[s]); + if (lrTmp) { + TLevelP levelTmp = lrTmp->loadInfo(); + std::vector frames; + getFrameIds(1, levelTmp->getFrameCount(), levelTmp, frames); + for (int f = 0; f < frames.size(); f++) + m_srcFrames.append(qMakePair(fps[s], frames[f])); + } + } + } + m_saveInFileFld->setPath(toQString(fps[0].getParentDir())); + + // update preview frames + m_previewFrameField->setRange(1, m_srcFrames.size()); + m_previewFrameField->setValue(1); + onPreviewFrameFieldValueChanged(true); +} + +//------------------------------------------------------------------- + +void SeparateColorsPopup::doSeparate(const TFilePath& source, int from, int to, + int framerate, + FrameTaskNotifier* frameNotifier, + bool do4Colors) { + QString msg; + + TLevelReaderP lr(source); + TLevelP level = lr->loadInfo(); + + TDimension res(0, 0); + std::string srcExt = source.getType(); + + std::vector frames; + getFrameIds(from, to, level, frames); + + TImageInfo* ii = (TImageInfo*)lr->getImageInfo(frames[0]); + TPropertyGroup* prop = ii->m_properties; + + // destination folder + TFilePath dstFolder(m_saveInFileFld->getPath().toStdWString()); + dstFolder = TApp::instance()->getCurrentScene()->getScene()->decodeFilePath( + dstFolder); + + // file type + std::string ext = m_fileFormatCombo->currentText().toStdString(); + + TFilePath dest_m = + source.withParentDir(dstFolder) + .withName(source.getName() + m_mainSuffixEdit->text().toStdString()) + .withType(ext); + TFilePath dest_c1 = + source.withParentDir(dstFolder) + .withName(source.getName() + m_sub1SuffixEdit->text().toStdString()) + .withType(ext); + TFilePath dest_c2 = + source.withParentDir(dstFolder) + .withName(source.getName() + m_sub2SuffixEdit->text().toStdString()) + .withType(ext); + TFilePath dest_c3 = + source.withParentDir(dstFolder) + .withName(source.getName() + m_sub3SuffixEdit->text().toStdString()) + .withType(ext); + + if (!TSystem::touchParentDir(dest_m)) { + QMessageBox::critical(this, tr("Critical"), + tr("Failed to access the destination folder!")); + return; + } + + TPropertyGroup* propForWrite = (m_matteGroupBox->isChecked()) ? 0 : prop; + + TLevelWriterP lw_m(dest_m, propForWrite); + TLevelWriterP lw_c1(dest_c1, propForWrite); + TLevelWriterP lw_c2(dest_c2, propForWrite); + TLevelWriterP lw_c3(dest_c3, propForWrite); + + lw_m->setFrameRate(framerate); + lw_c1->setFrameRate(framerate); + lw_c2->setFrameRate(framerate); + lw_c3->setFrameRate(framerate); + + bool outMain = m_outMainCB->isChecked(); + bool outSub1 = m_outSub1CB->isChecked(); + bool outSub2 = m_outSub2CB->isChecked(); + bool outSub3 = do4Colors; + + // for each frame + for (int i = 0; i < (int)frames.size(); i++) { + if (frameNotifier->abortTask()) break; + + TImageReaderP ir = lr->getFrameReader(frames[i]); + TRasterImageP img = ir->load(); + double xDpi, yDpi; + img->getDpi(xDpi, yDpi); + + TDimensionI dim = convert(img->getBBox()).getSize(); + + TRaster32P raster(dim); + raster = img->getRaster(); + raster->lock(); + + TRaster32P ras_m(dim); + ras_m->lock(); + TRaster32P ras_c1(dim); + ras_c1->lock(); + TRaster32P ras_c2(dim); + ras_c2->lock(); + + if (!do4Colors) { + doCompute(raster, dim, ras_m, ras_c1, ras_c2); + } else { + TRaster32P ras_c3(dim); + ras_c3->lock(); + doCompute(raster, dim, ras_m, ras_c1, ras_c2, ras_c3); + TRasterImageP newImg_c3(ras_c3); + newImg_c3->setDpi(xDpi, yDpi); + TImageWriterP iw_c3 = lw_c3->getFrameWriter(frames[i]); + iw_c3->save(newImg_c3); + ras_c3->unlock(); + } + + TRasterImageP newImg_m(ras_m); + TRasterImageP newImg_c1(ras_c1); + TRasterImageP newImg_c2(ras_c2); + + newImg_m->setDpi(xDpi, yDpi); + newImg_c1->setDpi(xDpi, yDpi); + newImg_c2->setDpi(xDpi, yDpi); + + if (outMain) { + TImageWriterP iw_m = lw_m->getFrameWriter(frames[i]); + iw_m->save(newImg_m); + } + if (outSub1) { + TImageWriterP iw_c1 = lw_c1->getFrameWriter(frames[i]); + iw_c1->save(newImg_c1); + } + if (outSub2) { + TImageWriterP iw_c2 = lw_c2->getFrameWriter(frames[i]); + iw_c2->save(newImg_c2); + } + + raster->unlock(); + ras_m->unlock(); + ras_c1->unlock(); + ras_c2->unlock(); + + // this notification is for updating the progress bar. + // thus, not frameId but the amount of finished frames is thrown. + frameNotifier->notifyFrameCompleted(100 * (i + 1) / frames.size()); + } +} + +//------------------------------------------------------------------- + +void SeparateColorsPopup::doPreview(TRaster32P& orgRas, TRaster32P& mainRas, + TRaster32P& sub1Ras, TRaster32P& sub2Ras, + TRaster32P& sub3Ras) { + if (m_srcFrames.isEmpty()) return; + + TPixel paperColor = m_paperColorField->getColor(); + TPixel mainColor = m_mainColorField->getColor(); + TPixel subColor1 = m_subColor1Field->getColor(); + TPixel subColor2 = m_subColor2Field->getColor(); + TPixel subColor3 = m_subColor3Field->getColor(); + + bool do4Colors = m_outSub3CB->isChecked(); + + // specify the transparent colors + bool showAlpha = m_showAlphaBtn->isChecked(); + m_separateSwatch->setTranspColors(mainColor, subColor1, subColor2, subColor3, + showAlpha); + + int frame = m_previewFrameField->getValue() - 1; + TFilePath fp = m_srcFrames[frame].first; + TFrameId fId = m_srcFrames[frame].second; + + TLevelReaderP lr(fp); + TImageReaderP ir = lr->getFrameReader(fId); + + TRasterImageP img = ir->load(); + TDimensionI dim = convert(img->getBBox()).getSize(); + + orgRas = img->getRaster(); + mainRas = TRaster32P(dim); + sub1Ras = TRaster32P(dim); + sub2Ras = TRaster32P(dim); + + if (!do4Colors) { + doCompute(orgRas, dim, mainRas, sub1Ras, sub2Ras, true); + } else { + sub3Ras = TRaster32P(dim); + doCompute(orgRas, dim, mainRas, sub1Ras, sub2Ras, sub3Ras, true); + } +} + +//------------------------------------------------------------------- + +void SeparateColorsPopup::doCompute(TRaster32P raster, TDimensionI& dim, + TRaster32P ras_m, TRaster32P ras_c1, + TRaster32P ras_c2, bool isPreview) { + TPixel paperColor = m_paperColorField->getColor(); + TPixel mainColor = m_mainColorField->getColor(); + TPixel subColor1 = m_subColor1Field->getColor(); + TPixel subColor2 = m_subColor2Field->getColor(); + + // alpha matting + float matteThres = (float)(m_matteThreshold->getValue()); + int matteRadius = m_matteRadius->getValue(); + bool doMatte = m_matteGroupBox->isChecked() && matteThres > 0.0f; + + // intensity of each color is to be stored + QVector3D* out_host; + TRasterGR8P out_host_ras(sizeof(QVector3D) * dim.lx * dim.ly, 1); + out_host_ras->lock(); + out_host = (QVector3D*)out_host_ras->getRawData(); + + QVector3D* src_host; + TRasterGR8P src_host_ras(sizeof(QVector3D) * dim.lx * dim.ly, 1); + src_host_ras->lock(); + src_host = (QVector3D*)src_host_ras->getRawData(); + + // matte information + uchar3* matte_host = nullptr; + TRasterGR8P matte_host_ras; + if (doMatte) { + matte_host_ras = TRasterGR8P(sizeof(uchar3) * dim.lx * dim.ly, 1); + matte_host_ras->lock(); + matte_host = (uchar3*)matte_host_ras->getRawData(); + } + + // input the souce image + QVector3D* chann_p = src_host; + for (int j = 0; j < dim.ly; j++) { + TPixel32* pix = raster->pixels(j); + for (int i = 0; i < dim.lx; i++, chann_p++, pix++) { + (*chann_p).setX((float)pix->r / (float)TPixel32::maxChannelValue); + (*chann_p).setY((float)pix->g / (float)TPixel32::maxChannelValue); + (*chann_p).setZ((float)pix->b / (float)TPixel32::maxChannelValue); + } + } + + QVector3D paper_xyz = myHls2Xyz(myRgb2Hls(myPix2Rgb(paperColor))); + QVector3D main_xyz = myHls2Xyz(myRgb2Hls(myPix2Rgb(mainColor))); + QVector3D sub1_xyz = myHls2Xyz(myRgb2Hls(myPix2Rgb(subColor1))); + QVector3D sub2_xyz = myHls2Xyz(myRgb2Hls(myPix2Rgb(subColor2))); + + // obtain the factors of plane equation : ax + by + cz = d + QVector4D pane = getPane(main_xyz, sub1_xyz, sub2_xyz); + + int maxThreadCount = + std::max(1, QThreadPool::globalInstance()->maxThreadCount() / 2); + int start = 0; + for (int t = 0; t < maxThreadCount; t++) { + int end = (int)std::round((float)(dim.lx * dim.ly * (t + 1)) / + (float)maxThreadCount); + + Separate3ColorsTask* task = new Separate3ColorsTask( + start, end, src_host, out_host, paper_xyz, main_xyz, sub1_xyz, sub2_xyz, + pane, (float)m_subColorAdjustFld->getValue(), + (float)m_borderSmoothnessFld->getValue()); + + QThreadPool::globalInstance()->start(task); + + start = end; + } + + QThreadPool::globalInstance()->waitForDone(); + + src_host_ras->unlock(); + + if (doMatte) { + // mask by threshold, then expand it + for (int i = 0; i < dim.lx * dim.ly; i++) { + maskByThres(i, out_host, matte_host, matteThres); + } + + for (int r = 0; r < matteRadius; r++) { + for (int y = 0; y < dim.ly; y++) { + for (int x = 0; x < dim.lx; x++) { + expandMask(x, y, matte_host, dim.lx, dim.ly, r); + } + } + } + } + + auto getChannelValue = [](unsigned char pencil_chanVal, + float ratio) -> unsigned char { + return (unsigned char)(std::round((float)pencil_chanVal * ratio)); + }; + + bool showMask = m_showMatteBtn->isChecked(); + auto getMatteColor = [&](TPixel32 lineColor) -> TPixel32 { + if (!isPreview || !showMask) return TPixel32::Transparent; + TPixelD lineColorD = toPixelD(lineColor); + double h, l, s; + rgb2hls(lineColorD.r, lineColorD.g, lineColorD.b, &h, &l, &s); + // if the specified color is reddish, use blue color for transparent area + if ((h <= 30.0 || 330.0 <= h) && s >= 0.2) + return BlueMaskColor; + else + return RedMaskColor; + }; + + TPixel32 matteColor_m = getMatteColor(mainColor); + TPixel32 matteColor_s1 = getMatteColor(subColor1); + TPixel32 matteColor_s2 = getMatteColor(subColor2); + + // show alpha channel + if (isPreview && m_showAlphaBtn->isChecked()) { + mainColor = TPixel::White; + subColor1 = TPixel::White; + subColor2 = TPixel::White; + } + + // compute result + QVector3D* ratio_p = out_host; + uchar3* matte_p = matte_host; + for (int j = 0; j < dim.ly; j++) { + TPixel32* pix_m = ras_m->pixels(j); + TPixel32* pix_c1 = ras_c1->pixels(j); + TPixel32* pix_c2 = ras_c2->pixels(j); + for (int i = 0; i < dim.lx; i++, ratio_p++, pix_m++, pix_c1++, pix_c2++) { + // main + if (!doMatte || (*matte_p).x > 0) { + float ratio_m = (*ratio_p).x(); + if (doMatte) + ratio_m *= + (float)(matteRadius + 1 - (int)(*matte_p).x) / (float)matteRadius; + ratio_m = std::min(1.0f, ratio_m); + + pix_m->r = getChannelValue(mainColor.r, ratio_m); + pix_m->g = getChannelValue(mainColor.g, ratio_m); + pix_m->b = getChannelValue(mainColor.b, ratio_m); + pix_m->m = getChannelValue(TPixel32::maxChannelValue, ratio_m); + } else { + *pix_m = matteColor_m; + } + + // sub1 + if (!doMatte || (*matte_p).y > 0) { + float ratio_s1 = (*ratio_p).y(); + if (doMatte) + ratio_s1 *= + (float)(matteRadius + 1 - (int)(*matte_p).y) / (float)matteRadius; + ratio_s1 = std::min(1.0f, ratio_s1); + + pix_c1->r = getChannelValue(subColor1.r, ratio_s1); + pix_c1->g = getChannelValue(subColor1.g, ratio_s1); + pix_c1->b = getChannelValue(subColor1.b, ratio_s1); + pix_c1->m = getChannelValue(TPixel32::maxChannelValue, ratio_s1); + } else { + *pix_c1 = matteColor_s1; + } + + // sub2 + if (!doMatte || (*matte_p).z > 0) { + float ratio_s2 = (*ratio_p).z(); + if (doMatte) + ratio_s2 *= + (float)(matteRadius + 1 - (int)(*matte_p).z) / (float)matteRadius; + ratio_s2 = std::min(1.0f, ratio_s2); + + pix_c2->r = getChannelValue(subColor2.r, ratio_s2); + pix_c2->g = getChannelValue(subColor2.g, ratio_s2); + pix_c2->b = getChannelValue(subColor2.b, ratio_s2); + pix_c2->m = getChannelValue(TPixel32::maxChannelValue, ratio_s2); + } else { + *pix_c2 = matteColor_s2; + } + + if (doMatte) matte_p++; + } + } + out_host_ras->unlock(); + + if (doMatte) matte_host_ras->unlock(); +} + +//------------------------------------------------------------------- +// 4colors version + +void SeparateColorsPopup::doCompute(TRaster32P raster, TDimensionI& dim, + TRaster32P ras_m, TRaster32P ras_c1, + TRaster32P ras_c2, TRaster32P ras_c3, + bool isPreview) { + TPixel paperColor = m_paperColorField->getColor(); + TPixel mainColor = m_mainColorField->getColor(); + TPixel subColor1 = m_subColor1Field->getColor(); + TPixel subColor2 = m_subColor2Field->getColor(); + TPixel subColor3 = m_subColor3Field->getColor(); + + // alpha matting + float matteThres = (float)(m_matteThreshold->getValue()); + int matteRadius = m_matteRadius->getValue(); + bool doMatte = m_matteGroupBox->isChecked() && matteThres > 0.0f; + + // intensity of each color is to be stored + QVector4D* out_host; + TRasterGR8P out_host_ras(sizeof(QVector4D) * dim.lx * dim.ly, 1); + out_host_ras->lock(); + out_host = (QVector4D*)out_host_ras->getRawData(); + + QVector3D* src_host; + TRasterGR8P src_host_ras(sizeof(QVector3D) * dim.lx * dim.ly, 1); + src_host_ras->lock(); + src_host = (QVector3D*)src_host_ras->getRawData(); + + // matte information + uchar4* matte_host = nullptr; + TRasterGR8P matte_host_ras; + if (doMatte) { + matte_host_ras = TRasterGR8P(sizeof(uchar4) * dim.lx * dim.ly, 1); + matte_host_ras->lock(); + matte_host = (uchar4*)matte_host_ras->getRawData(); + } + + // input the souce image + QVector3D* chann_p = src_host; + for (int j = 0; j < dim.ly; j++) { + TPixel32* pix = raster->pixels(j); + for (int i = 0; i < dim.lx; i++, chann_p++, pix++) { + (*chann_p).setX((float)pix->r / (float)TPixel32::maxChannelValue); + (*chann_p).setY((float)pix->g / (float)TPixel32::maxChannelValue); + (*chann_p).setZ((float)pix->b / (float)TPixel32::maxChannelValue); + } + } + + QVector3D paper_xyz = myHls2Xyz(myRgb2Hls(myPix2Rgb(paperColor))); + QVector3D main_xyz = myHls2Xyz(myRgb2Hls(myPix2Rgb(mainColor))); + QVector3D sub1_xyz = myHls2Xyz(myRgb2Hls(myPix2Rgb(subColor1))); + QVector3D sub2_xyz = myHls2Xyz(myRgb2Hls(myPix2Rgb(subColor2))); + QVector3D sub3_xyz = myHls2Xyz(myRgb2Hls(myPix2Rgb(subColor3))); + + // obtain the plane equations intersecting three colors + // plane of main, sub1, and sub2 + QVector4D pane_m12 = getPane(main_xyz, sub1_xyz, sub2_xyz); + // plane of main, sub1, and sub3 + QVector4D pane_m13 = getPane(main_xyz, sub1_xyz, sub3_xyz); + // plane of main, sub2, and sub3 + QVector4D pane_m23 = getPane(main_xyz, sub2_xyz, sub3_xyz); + // plane of sub1, sub2, and sub3 + QVector4D pane_123 = getPane(sub1_xyz, sub2_xyz, sub3_xyz); + + // vector paper -> main color + QVector3D vec_pm = main_xyz - paper_xyz; + + float de = pane_123.x() * vec_pm.x() + pane_123.y() * vec_pm.y() + + pane_123.z() * vec_pm.z(); + float t = (pane_123.w() - + (pane_123.x() * paper_xyz.x() + pane_123.y() * paper_xyz.y() + + pane_123.z() * paper_xyz.z())) / + de; + + // intersection of paper-main vector and the plane 123 + QVector3D R = paper_xyz + t * vec_pm; + + int maxThreadCount = + std::max(1, QThreadPool::globalInstance()->maxThreadCount() / 2); + int start = 0; + for (int t = 0; t < maxThreadCount; t++) { + int end = (int)std::round((float)(dim.lx * dim.ly * (t + 1)) / + (float)maxThreadCount); + + Separate4ColorsTask* task = new Separate4ColorsTask( + start, end, src_host, out_host, paper_xyz, main_xyz, sub1_xyz, sub2_xyz, + sub3_xyz, pane_m12, pane_m13, pane_m23, pane_123, R, + (float)m_subColorAdjustFld->getValue(), + (float)m_borderSmoothnessFld->getValue()); + + QThreadPool::globalInstance()->start(task); + + start = end; + } + + QThreadPool::globalInstance()->waitForDone(); + + src_host_ras->unlock(); + + if (doMatte) { + // mask by threshold, then expand it + for (int i = 0; i < dim.lx * dim.ly; i++) { + maskByThres(i, out_host, matte_host, matteThres); + } + + for (int r = 0; r < matteRadius; r++) { + for (int y = 0; y < dim.ly; y++) { + for (int x = 0; x < dim.lx; x++) { + expandMask(x, y, matte_host, dim.lx, dim.ly, r); + } + } + } + } + + auto getChannelValue = [](unsigned char pencil_chanVal, + float ratio) -> unsigned char { + return (unsigned char)(std::round((float)pencil_chanVal * ratio)); + }; + + bool showMask = m_showMatteBtn->isChecked(); + auto getMatteColor = [&](TPixel32 lineColor) -> TPixel32 { + if (!isPreview || !showMask) return TPixel32::Transparent; + TPixelD lineColorD = toPixelD(lineColor); + double h, l, s; + rgb2hls(lineColorD.r, lineColorD.g, lineColorD.b, &h, &l, &s); + // if the specified color is reddish, use blue color for transparent area + if ((h <= 30.0 || 330.0 <= h) && s >= 0.2) + return BlueMaskColor; + else + return RedMaskColor; + }; + + TPixel32 matteColor_m = getMatteColor(mainColor); + TPixel32 matteColor_s1 = getMatteColor(subColor1); + TPixel32 matteColor_s2 = getMatteColor(subColor2); + TPixel32 matteColor_s3 = getMatteColor(subColor3); + + // show alpha channel + if (isPreview && m_showAlphaBtn->isChecked()) { + mainColor = TPixel::White; + subColor1 = TPixel::White; + subColor2 = TPixel::White; + subColor3 = TPixel::White; + } + + // compute result + QVector4D* ratio_p = out_host; + uchar4* matte_p = matte_host; + for (int j = 0; j < dim.ly; j++) { + TPixel32* pix_m = ras_m->pixels(j); + TPixel32* pix_c1 = ras_c1->pixels(j); + TPixel32* pix_c2 = ras_c2->pixels(j); + TPixel32* pix_c3 = ras_c3->pixels(j); + for (int i = 0; i < dim.lx; + i++, ratio_p++, pix_m++, pix_c1++, pix_c2++, pix_c3++) { + // main + if (!doMatte || (*matte_p).x > 0) { + float ratio_m = (*ratio_p).x(); + if (doMatte) + ratio_m *= + (float)(matteRadius + 1 - (int)(*matte_p).x) / (float)matteRadius; + ratio_m = std::min(1.0f, ratio_m); + + pix_m->r = getChannelValue(mainColor.r, ratio_m); + pix_m->g = getChannelValue(mainColor.g, ratio_m); + pix_m->b = getChannelValue(mainColor.b, ratio_m); + pix_m->m = getChannelValue(TPixel32::maxChannelValue, ratio_m); + } else { + *pix_m = matteColor_m; + } + + // sub1 + if (!doMatte || (*matte_p).y > 0) { + float ratio_s1 = (*ratio_p).y(); + if (doMatte) + ratio_s1 *= + (float)(matteRadius + 1 - (int)(*matte_p).y) / (float)matteRadius; + ratio_s1 = std::min(1.0f, ratio_s1); + + pix_c1->r = getChannelValue(subColor1.r, ratio_s1); + pix_c1->g = getChannelValue(subColor1.g, ratio_s1); + pix_c1->b = getChannelValue(subColor1.b, ratio_s1); + pix_c1->m = getChannelValue(TPixel32::maxChannelValue, ratio_s1); + } else { + *pix_c1 = matteColor_s1; + } + + // sub2 + if (!doMatte || (*matte_p).z > 0) { + float ratio_s2 = (*ratio_p).z(); + if (doMatte) + ratio_s2 *= + (float)(matteRadius + 1 - (int)(*matte_p).z) / (float)matteRadius; + ratio_s2 = std::min(1.0f, ratio_s2); + + pix_c2->r = getChannelValue(subColor2.r, ratio_s2); + pix_c2->g = getChannelValue(subColor2.g, ratio_s2); + pix_c2->b = getChannelValue(subColor2.b, ratio_s2); + pix_c2->m = getChannelValue(TPixel32::maxChannelValue, ratio_s2); + } else { + *pix_c2 = matteColor_s2; + } + + // sub3 + if (!doMatte || (*matte_p).w > 0) { + float ratio_s3 = (*ratio_p).w(); + if (doMatte) + ratio_s3 *= + (float)(matteRadius + 1 - (int)(*matte_p).w) / (float)matteRadius; + ratio_s3 = std::min(1.0f, ratio_s3); + + pix_c3->r = getChannelValue(subColor3.r, ratio_s3); + pix_c3->g = getChannelValue(subColor3.g, ratio_s3); + pix_c3->b = getChannelValue(subColor3.b, ratio_s3); + pix_c3->m = getChannelValue(TPixel32::maxChannelValue, ratio_s3); + } else { + *pix_c2 = matteColor_s3; + } + + if (doMatte) matte_p++; + } + } + out_host_ras->unlock(); + + if (doMatte) matte_host_ras->unlock(); +} + +void SeparateColorsPopup::showEvent(QShowEvent* e) { onPreviewBtnPressed(); } + +//------------------------------------------------------------------- +// store the current value to user env file + +void SeparateColorsPopup::hideEvent(QHideEvent* e) { + TPixel32 col = m_paperColorField->getColor(); + SeparateColorsPopup_PaperColor = + QColor(col.r, col.g, col.b).name().toStdString(); + col = m_mainColorField->getColor(); + SeparateColorsPopup_MainColor = + QColor(col.r, col.g, col.b).name().toStdString(); + col = m_subColor1Field->getColor(); + SeparateColorsPopup_SubColor1 = + QColor(col.r, col.g, col.b).name().toStdString(); + col = m_subColor2Field->getColor(); + SeparateColorsPopup_SubColor2 = + QColor(col.r, col.g, col.b).name().toStdString(); + col = m_subColor3Field->getColor(); + SeparateColorsPopup_SubColor3 = + QColor(col.r, col.g, col.b).name().toStdString(); + + SeparateColorsPopup_SubAdjust = m_subColorAdjustFld->getValue(); + SeparateColorsPopup_BorderSmooth = m_borderSmoothnessFld->getValue(); + SeparateColorsPopup_OutMain = (int)m_outMainCB->isChecked(); + SeparateColorsPopup_OutSub1 = (int)m_outSub1CB->isChecked(); + SeparateColorsPopup_OutSub2 = (int)m_outSub2CB->isChecked(); + SeparateColorsPopup_OutSub3 = (int)m_outSub3CB->isChecked(); + + SeparateColorsPopup_MainSuffix = m_mainSuffixEdit->text().toStdString(); + SeparateColorsPopup_Sub1Suffix = m_sub1SuffixEdit->text().toStdString(); + SeparateColorsPopup_Sub2Suffix = m_sub2SuffixEdit->text().toStdString(); + SeparateColorsPopup_Sub3Suffix = m_sub3SuffixEdit->text().toStdString(); + + SeparateColorsPopup_doMatte = (int)m_matteGroupBox->isChecked(); + SeparateColorsPopup_MatteThreshold = m_matteThreshold->getValue(); + SeparateColorsPopup_MatteRadius = m_matteRadius->getValue(); + + SeparateColorsPopup_FileFormat = + m_fileFormatCombo->currentText().toStdString(); +} + +//------------------------------------------------------------------- + +void SeparateColorsPopup::separate() { + if (!m_outMainCB->isChecked() && !m_outSub1CB->isChecked() && + !m_outSub2CB->isChecked() && !m_outSub3CB->isChecked()) + return; + + m_isConverting = true; + m_progressDialog->show(); + + m_notifier->reset(); + int from = -1; + int to = -1; + + for (int i = 0; !m_notifier->abortTask() && i < m_srcFilePaths.size(); i++) { + QString label; + TFilePath dstFilePath; + if (m_srcFilePaths.size() == 1) { + from = m_fromFld->text().toInt(); + to = m_toFld->text().toInt(); + label = QString( + tr("Separating %1") + .arg(QString::fromStdString(m_srcFilePaths[i].getLevelName()))); + } else { + TLevelP levelTmp; + TLevelReaderP lrTmp = TLevelReaderP(m_srcFilePaths[i]); + if (lrTmp) levelTmp = lrTmp->loadInfo(); + from = 1; + to = levelTmp->getFrameCount(); + label = QString( + tr("Converting level %1 of %2: %3") + .arg(i + 1) + .arg(m_srcFilePaths.size()) + .arg(QString::fromStdString(m_srcFilePaths[i].getLevelName()))); + } + + m_progressDialog->setLabelText(label); + + m_notifier->notifyFrameCompleted(0); + + TOutputProperties* oprop = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getProperties() + ->getOutputProperties(); + int framerate = oprop->getFrameRate(); + + doSeparate(m_srcFilePaths[i], from, to, framerate, m_notifier, + m_outSub3CB->isChecked()); + } + + onSeparateFinished(); +} + +//------------------------------------------------------------------- + +void SeparateColorsPopup::onPreviewBtnPressed() { + TRaster32P orgRas, mainRas, sub1Ras, sub2Ras, sub3Ras; + doPreview(orgRas, mainRas, sub1Ras, sub2Ras, sub3Ras); + if (m_outSub3CB->isChecked()) + m_separateSwatch->setRaster(orgRas, mainRas, sub1Ras, sub2Ras, sub3Ras); + else + m_separateSwatch->setRaster(orgRas, mainRas, sub1Ras, sub2Ras); +} + +//------------------------------------------------------------------- + +void SeparateColorsPopup::onPreviewFrameFieldValueChanged(bool isDragging) { + int frame = m_previewFrameField->getValue(); + if (frame > m_srcFrames.size()) return; + + TFilePath fp = m_srcFrames.at(frame - 1).first; + TFrameId fId = m_srcFrames.at(frame - 1).second; + + m_previewFrameLabel->setText(QString::fromStdWString( + fp.withFrame(fId).withoutParentDir().getWideString())); + + onChange(isDragging); +} + +//------------------------------------------------------------------- + +void SeparateColorsPopup::onChange(bool isDragging) { + // if autopreview is activated + if (m_autoBtn->isChecked() && !isDragging && !m_srcFrames.isEmpty()) + onPreviewBtnPressed(); +} + +//============================================================================= +// SeparateColorsPopupCommand +//----------------------------------------------------------------------------- + +OpenPopupCommandHandler openSeparateColorsPopup( + MI_SeparateColors); diff --git a/toonz/sources/toonz/separatecolorspopup.h b/toonz/sources/toonz/separatecolorspopup.h new file mode 100644 index 0000000..6b04674 --- /dev/null +++ b/toonz/sources/toonz/separatecolorspopup.h @@ -0,0 +1,185 @@ +#pragma once +//============================================================================= +// SeparateColorsPopup +//----------------------------------------------------------------------------- +#ifndef SEPARATECOLORSPOPUP_H +#define SEPARATECOLORSPOPUP_H + +#include "tfilepath.h" +#include "tpixel.h" +#include "traster.h" +#include "toonzqt/dvdialog.h" + +#include +#include +#include +#include +#include + +class QLabel; +class QCheckBox; +class QGroupBox; +class QComboBox; + +class SeparateSwatch; +namespace DVGui { +class IntLineEdit; +class FileField; +class LineEdit; +class ColorField; +class ProgressDialog; +class CheckBox; +class DoubleField; +class IntField; +} + +namespace ImageUtils { +class FrameTaskNotifier; +} + +using namespace DVGui; +using namespace ImageUtils; + +//---------------------------------- + +class Separate4ColorsTask : public QRunnable { + int m_from, m_to; + QVector3D* m_src; + QVector4D* m_out; + const QVector3D m_paper_xyz, m_main_xyz, m_sub1_xyz, m_sub2_xyz, m_sub3_xyz; + const QVector4D m_pane_m12, m_pane_m13, m_pane_m23, m_pane_123; + const QVector3D m_R; + const float m_mainAdjust; + const float m_borderRatio; + + void run(); + +public: + Separate4ColorsTask(int from, int to, QVector3D* src, QVector4D* out, + const QVector3D paper_xyz, const QVector3D main_xyz, + const QVector3D sub1_xyz, const QVector3D sub2_xyz, + const QVector3D sub3_xyz, const QVector4D pane_m12, + const QVector4D pane_m13, const QVector4D pane_m23, + const QVector4D pane_123, const QVector3D R, + const float mainAdjust, const float borderRatio) + : m_from(from) + , m_to(to) + , m_src(src) + , m_out(out) + , m_paper_xyz(paper_xyz) + , m_main_xyz(main_xyz) + , m_sub1_xyz(sub1_xyz) + , m_sub2_xyz(sub2_xyz) + , m_sub3_xyz(sub3_xyz) + , m_pane_m12(pane_m12) + , m_pane_m13(pane_m13) + , m_pane_m23(pane_m23) + , m_pane_123(pane_123) + , m_R(R) + , m_mainAdjust(mainAdjust) + , m_borderRatio(borderRatio) {} +}; + +//---------------------------------- + +class Separate3ColorsTask : public QRunnable { + int m_from, m_to; + QVector3D *m_src, *m_out; + const QVector3D m_paper_xyz, m_main_xyz, m_sub1_xyz, m_sub2_xyz; + const QVector4D m_pane; + const float m_mainAdjust; + const float m_borderRatio; + + void run(); + +public: + Separate3ColorsTask(int from, int to, QVector3D* src, QVector3D* out, + const QVector3D paper_xyz, const QVector3D main_xyz, + const QVector3D sub1_xyz, const QVector3D sub2_xyz, + const QVector4D pane, const float mainAdjust, + const float borderRatio) + : m_from(from) + , m_to(to) + , m_src(src) + , m_out(out) + , m_paper_xyz(paper_xyz) + , m_main_xyz(main_xyz) + , m_sub1_xyz(sub1_xyz) + , m_sub2_xyz(sub2_xyz) + , m_pane(pane) + , m_mainAdjust(mainAdjust) + , m_borderRatio(borderRatio) {} +}; + +//---------------------------------- + +class SeparateColorsPopup : public DVGui::Dialog { + Q_OBJECT + + FrameTaskNotifier* m_notifier; + QPushButton *m_previewBtn, *m_okBtn, *m_cancelBtn, *m_autoBtn; + IntLineEdit *m_fromFld, *m_toFld; + FileField* m_saveInFileFld; + QComboBox* m_fileFormatCombo; + + DoubleField *m_subColorAdjustFld, *m_borderSmoothnessFld; + + QGroupBox* m_matteGroupBox; + DoubleField* m_matteThreshold; + IntField* m_matteRadius; + + ColorField *m_paperColorField, *m_mainColorField, *m_subColor1Field, + *m_subColor2Field, *m_subColor3Field; + + QCheckBox *m_outMainCB, *m_outSub1CB, *m_outSub2CB, *m_outSub3CB; + LineEdit *m_mainSuffixEdit, *m_sub1SuffixEdit, *m_sub2SuffixEdit, + *m_sub3SuffixEdit; + + IntField* m_previewFrameField; + QLabel* m_previewFrameLabel; + QVector> m_srcFrames; + + QPushButton *m_pickBtn, *m_showMatteBtn, *m_showAlphaBtn; + SeparateSwatch* m_separateSwatch; + + ProgressDialog* m_progressDialog; + std::vector m_srcFilePaths; + bool m_isConverting; + + void doSeparate(const TFilePath& source, int from, int to, int framerate, + FrameTaskNotifier* frameNotifier, bool do4Colors); + + void doCompute(TRaster32P raster, TDimensionI& dim, TRaster32P ras_m, + TRaster32P ras_c1, TRaster32P ras_c2, bool isPreview = false); + + void doCompute(TRaster32P raster, TDimensionI& dim, TRaster32P ras_m, + TRaster32P ras_c1, TRaster32P ras_c2, TRaster32P ras_c3, + bool isPreview = false); + + void doPreview(TRaster32P& orgRas, TRaster32P& mainRas, TRaster32P& sub1Ras, + TRaster32P& sub2Ras, TRaster32P& sub3Ras); + +public: + SeparateColorsPopup(); + ~SeparateColorsPopup(); + void setFiles(const std::vector& fps); + bool isConverting(); + +protected: + void showEvent(QShowEvent* e); + // store the current value to user env file + void hideEvent(QHideEvent* e); + +public slots: + // starts the convertion + void separate(); + void onSeparateFinished(); + void onPreviewBtnPressed(); + void onPreviewFrameFieldValueChanged(bool isDragging); + + void onChange(bool isDragging); + void onToggle() { onChange(false); } + void onColorChange(const TPixel32&, bool isDragging) { onChange(isDragging); } +}; + +#endif \ No newline at end of file diff --git a/toonz/sources/toonz/separatecolorsswatch.cpp b/toonz/sources/toonz/separatecolorsswatch.cpp new file mode 100644 index 0000000..d02e21a --- /dev/null +++ b/toonz/sources/toonz/separatecolorsswatch.cpp @@ -0,0 +1,473 @@ +#include "separatecolorsswatch.h" + +#include "tapp.h" + +#include "toonzqt/styleeditor.h" +#include "toonzqt/menubarcommand.h" +#include "toonzqt/viewcommandids.h" +#include "toonzqt/gutil.h" +#include "toonzqt/dvdialog.h" + +#include "toonz/tcleanupper.h" + +#include "tools/stylepicker.h" +#include "tools/toolcommandids.h" + +#include "trasterimage.h" +#include "tpixelutils.h" +#include "trop.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------- +namespace { +#define ZOOMLEVELS 33 +#define NOZOOMINDEX 20 +double ZoomFactors[ZOOMLEVELS] = { + 0.001, 0.002, 0.003, 0.004, 0.005, 0.007, 0.01, 0.015, 0.02, 0.03, 0.04, + 0.05, 0.0625, 0.0833, 0.125, 0.167, 0.25, 0.333, 0.5, 0.667, 1, 2, + 3, 4, 5, 6, 7, 8, 12, 16, 24, 32, 40}; + +double getQuantizedZoomFactor(double zf, bool forward) { + if (forward && (zf > ZoomFactors[ZOOMLEVELS - 1] || + areAlmostEqual(zf, ZoomFactors[ZOOMLEVELS - 1], 1e-5))) + return zf; + else if (!forward && + (zf < ZoomFactors[0] || areAlmostEqual(zf, ZoomFactors[0], 1e-5))) + return zf; + + assert((!forward && zf > ZoomFactors[0]) || + (forward && zf < ZoomFactors[ZOOMLEVELS - 1])); + int i = 0; + for (i = 0; i <= ZOOMLEVELS - 1; i++) + if (areAlmostEqual(zf, ZoomFactors[i], 1e-5)) zf = ZoomFactors[i]; + + if (forward && zf < ZoomFactors[0]) + return ZoomFactors[0]; + else if (!forward && zf > ZoomFactors[ZOOMLEVELS - 1]) + return ZoomFactors[ZOOMLEVELS - 1]; + + for (i = 0; i < ZOOMLEVELS - 1; i++) + if (ZoomFactors[i + 1] - zf >= 0 && zf - ZoomFactors[i] >= 0) { + if (forward && ZoomFactors[i + 1] == zf) + return ZoomFactors[i + 2]; + else if (!forward && ZoomFactors[i] == zf) + return ZoomFactors[i - 1]; + else + return forward ? ZoomFactors[i + 1] : ZoomFactors[i]; + } + return ZoomFactors[NOZOOMINDEX]; +} + +} // namespace +//------------------------------------------------------------------- + +SeparateSwatchArea::SeparateSwatchArea(SeparateSwatch *parent, SWATCH_TYPE type) + : QWidget(parent) + , m_type(type) + , m_sw(parent) + , m_transparentColor(64, 64, 64) { + setMinimumHeight(100); + + // The following helps in re-establishing focus for wheel events + setFocusPolicy(Qt::WheelFocus); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); +} + +//------------------------------------------------------------------- + +void SeparateSwatchArea::mousePressEvent(QMouseEvent *event) { + if (!m_sw->m_mainRaster || m_sw->m_lx == 0 || m_sw->m_ly == 0) return; + + m_pos = event->pos(); + + // return if there is no raster + if (!m_r) { + event->ignore(); + m_panning = false; + return; + } + + // panning with drag + m_panning = true; + + event->ignore(); +} + +//----------------------------------------------------------------------------- + +void SeparateSwatchArea::keyPressEvent(QKeyEvent *event) { + if (!m_sw->m_mainRaster || m_sw->m_lx == 0 || m_sw->m_ly == 0) return; + + QString zoomInTxt = + CommandManager::instance()->getAction(V_ZoomIn)->shortcut().toString(); + QString zoomOutTxt = + CommandManager::instance()->getAction(V_ZoomOut)->shortcut().toString(); + + bool isZoomIn = + (QString::compare(event->text(), zoomInTxt, Qt::CaseInsensitive) == 0); + bool isZoomOut = + (QString::compare(event->text(), zoomOutTxt, Qt::CaseInsensitive) == 0); + + int key = event->key(); + + if (key != '+' && key != '-' && key != '0' && !isZoomIn && !isZoomOut) return; + + bool reset = (key == '0'); + bool forward = (key == '+' || isZoomIn); + + double currZoomScale = sqrt(m_sw->m_viewAff.det()); + double factor = reset ? 1 : getQuantizedZoomFactor(currZoomScale, forward); + + double minZoom = std::min((double)m_sw->m_lx / m_sw->m_mainRaster->getLx(), + (double)m_sw->m_ly / m_sw->m_mainRaster->getLy()); + if ((!forward && factor < minZoom) || (forward && factor > 40.0)) return; + + TPointD delta(0.5 * width(), 0.5 * height()); + m_sw->m_viewAff = (TTranslation(delta) * TScale(factor / currZoomScale) * + TTranslation(-delta)) * + m_sw->m_viewAff; + + m_sw->m_orgSwatch->updateRaster(); + m_sw->m_mainSwatch->updateRaster(); + m_sw->m_sub1Swatch->updateRaster(); + m_sw->m_sub2Swatch->updateRaster(); + m_sw->m_sub3Swatch->updateRaster(); +} + +//----------------------------------------------------------------------------- +void SeparateSwatchArea::mouseReleaseEvent(QMouseEvent *event) { + m_sw->m_mainSwatch->updateRaster(); + m_sw->m_sub1Swatch->updateRaster(); + m_sw->m_sub2Swatch->updateRaster(); + m_sw->m_sub3Swatch->updateRaster(); + m_panning = false; +} + +//---------------------------------------------------------------- +void SeparateSwatchArea::mouseMoveEvent(QMouseEvent *event) { + if (!m_panning) return; + + TPoint curPos = TPoint(event->pos().x(), event->pos().y()); + + QPoint delta = event->pos() - m_pos; + if (delta == QPoint()) return; + TAffine oldAff = m_sw->m_viewAff; + m_sw->m_viewAff = TTranslation(delta.x(), -delta.y()) * m_sw->m_viewAff; + + m_sw->m_orgSwatch->updateRaster(); + m_sw->m_mainSwatch->updateRaster(true); + m_sw->m_sub1Swatch->updateRaster(true); + m_sw->m_sub2Swatch->updateRaster(true); + m_sw->m_sub3Swatch->updateRaster(true); + + m_pos = event->pos(); +} + +//--------------------------------------------------------------- + +void SeparateSwatchArea::updateSeparated(bool dragging) { + TAffine aff = getFinalAff(); + + TRect rectToCompute = convert(aff.inv() * convert(m_r->getBounds())); + TRect outRect = m_sw->m_mainRaster->getBounds() * rectToCompute; + + if (outRect.isEmpty()) return; +} + +//--------------------------------------------------------------------------------- + +TAffine SeparateSwatchArea::getFinalAff() { + return m_sw->m_viewAff * + TAffine().place(m_sw->m_mainRaster->getCenterD(), m_r->getCenterD()); +} + +//------------------------------------------------------------------------------ +void SeparateSwatchArea::updateRaster(bool dragging) { + if (isHidden() || m_sw->m_lx == 0 || m_sw->m_ly == 0) return; + + if (!m_r || m_r->getLx() != m_sw->m_lx || m_r->getLy() != m_sw->m_ly) + m_r = TRaster32P(m_sw->m_lx, m_sw->m_ly); + + if (!m_sw->m_mainRaster) + m_r->fill(TPixel(200, 200, 200)); + else { + m_r->fill(m_transparentColor); + switch (m_type) { + case Original: + TRop::quickPut(m_r, m_sw->m_orgRaster, getFinalAff()); + break; + case Main: + TRop::quickPut(m_r, m_sw->m_mainRaster, getFinalAff()); + break; + case Sub1: + TRop::quickPut(m_r, m_sw->m_sub1Raster, getFinalAff()); + break; + case Sub2: + TRop::quickPut(m_r, m_sw->m_sub2Raster, getFinalAff()); + break; + case Sub3: + TRop::quickPut(m_r, m_sw->m_sub3Raster, getFinalAff()); + break; + } + } + if (dragging) + repaint(); + else + update(); +} + +//---------------------------------------------------------------------------------- + +void SeparateSwatchArea::paintEvent(QPaintEvent *event) { + QPainter p(this); + if (!m_r) + p.fillRect(rect(), QBrush(QColor(200, 200, 200))); + else + p.drawImage(rect(), rasterToQImage(m_r)); +} + +//----------------------------------------------------------------- + +void SeparateSwatchArea::wheelEvent(QWheelEvent *event) { + if (!m_sw->m_mainSwatch || m_sw->m_lx == 0 || m_sw->m_ly == 0) return; + + int step = event->delta() > 0 ? 120 : -120; + double factor = exp(0.001 * step); + if (factor == 1.0) return; + double scale = m_sw->m_viewAff.det(); + double minZoom = std::min((double)m_sw->m_lx / m_sw->m_mainRaster->getLx(), + (double)m_sw->m_ly / m_sw->m_mainRaster->getLy()); + if ((factor < 1 && sqrt(scale) < minZoom) || (factor > 1 && scale > 1200.0)) + return; + + TPointD delta(event->pos().x(), height() - event->pos().y()); + m_sw->m_viewAff = + (TTranslation(delta) * TScale(factor) * TTranslation(-delta)) * + m_sw->m_viewAff; + + m_sw->m_orgSwatch->updateRaster(); + m_sw->m_mainSwatch->updateRaster(); + m_sw->m_sub1Swatch->updateRaster(); + m_sw->m_sub2Swatch->updateRaster(); + m_sw->m_sub3Swatch->updateRaster(); +} + +//------------------------------------------------------------------------------- +void SeparateSwatchArea::setTranspColor(TPixel32 &lineColor, bool showAlpha) { + if (showAlpha) { + m_transparentColor = TPixel32::Black; + return; + } + + TPixelD lineColorD = toPixelD(lineColor); + double h, l, s; + rgb2hls(lineColorD.r, lineColorD.g, lineColorD.b, &h, &l, &s); + // if the specified color is reddish, use blue color for transparent area + if (l < 0.8) + m_transparentColor = TPixel32::White; + else + m_transparentColor = TPixel32(64, 64, 64); +} + +//------------------------------------------------------------------------------- +// focus on mouse enter + +void SeparateSwatchArea::enterEvent(QEvent *event) { + setFocus(); + event->accept(); +} + +//===================================================================== + +SeparateSwatch::SeparateSwatch(QWidget *parent, int lx, int ly) + : QWidget(parent), m_lx(lx), m_ly(ly), m_enabled(false), m_viewAff() { + m_orgSwatch = new SeparateSwatchArea(this, Original); + m_mainSwatch = new SeparateSwatchArea(this, Main); + m_sub1Swatch = new SeparateSwatchArea(this, Sub1); + m_sub2Swatch = new SeparateSwatchArea(this, Sub2); + m_sub3Swatch = new SeparateSwatchArea(this, Sub3); + + m_sub3Label = new QLabel(tr("Sub Color 3"), this); + + QGridLayout *mainLay = new QGridLayout(); + mainLay->setHorizontalSpacing(2); + mainLay->setVerticalSpacing(2); + { + mainLay->addWidget(new QLabel(tr("Original"), this), 0, 0); + mainLay->addWidget(new QLabel(tr("Main Color"), this), 0, 1); + + mainLay->addWidget(m_orgSwatch, 1, 0); + mainLay->addWidget(m_mainSwatch, 1, 1); + + mainLay->addWidget(new QLabel(tr("Sub Color 1"), this), 2, 0); + mainLay->addWidget(new QLabel(tr("Sub Color 2"), this), 2, 1); + mainLay->addWidget(m_sub3Label, 2, 2); + + mainLay->addWidget(m_sub1Swatch, 3, 0); + mainLay->addWidget(m_sub2Swatch, 3, 1); + mainLay->addWidget(m_sub3Swatch, 3, 2); + } + mainLay->setRowStretch(0, 0); + mainLay->setRowStretch(1, 1); + mainLay->setRowStretch(2, 0); + mainLay->setRowStretch(3, 1); + setLayout(mainLay); + + m_sub3Swatch->setVisible(false); + m_sub3Label->setVisible(false); +} + +//--------------------------------------------------------------------- + +void SeparateSwatch::enable(bool state) { + m_enabled = state; + if (!m_enabled) { + m_orgRaster = TRasterP(); + m_mainRaster = TRasterP(); + m_sub1Raster = TRasterP(); + m_sub2Raster = TRasterP(); + m_sub3Raster = TRasterP(); + + m_viewAff = TAffine(); + + m_orgSwatch->updateRaster(); + m_mainSwatch->updateRaster(); + m_sub1Swatch->updateRaster(); + m_sub2Swatch->updateRaster(); + m_sub3Swatch->updateRaster(); + } +} + +//---------------------------------------------------------------- + +void SeparateSwatch::resizeEvent(QResizeEvent *event) { + QSize s = m_orgSwatch->size(); + m_lx = s.width(); + m_ly = s.height(); + + m_orgSwatch->setMinimumHeight(0); + m_mainSwatch->setMinimumHeight(0); + m_sub1Swatch->setMinimumHeight(0); + m_sub2Swatch->setMinimumHeight(0); + m_sub3Swatch->setMinimumHeight(0); + + m_orgSwatch->updateRaster(); + m_mainSwatch->updateRaster(); + m_sub1Swatch->updateRaster(); + m_sub2Swatch->updateRaster(); + m_sub3Swatch->updateRaster(); + + QWidget::resizeEvent(event); +} + +//--------------------------------------------------- + +bool SeparateSwatch::isEnabled() { return isVisible() && m_enabled; } + +//------------------------------------------------------------ +void SeparateSwatch::setRaster(TRasterP orgRas, TRasterP mainRas, + TRasterP sub1Ras, TRasterP sub2Ras) { + m_sub3Swatch->setVisible(false); + m_sub3Label->setVisible(false); + + if (!isVisible()) { + m_orgRaster = TRasterP(); + m_mainRaster = TRasterP(); + m_sub1Raster = TRasterP(); + m_sub2Raster = TRasterP(); + return; + } + + // obtain the size here in order to avoid the image shrink + // when activating / deactivating sub3 color. + qApp->processEvents(); + QSize s = m_orgSwatch->size(); + m_lx = s.width(); + m_ly = s.height(); + + m_orgRaster = orgRas; + m_mainRaster = mainRas; + m_sub1Raster = sub1Ras; + m_sub2Raster = sub2Ras; + + m_orgSwatch->updateRaster(); + m_mainSwatch->updateRaster(); + m_sub1Swatch->updateRaster(); + m_sub2Swatch->updateRaster(); +} + +//------------------------------------------------------------ +// 4 colors version + +void SeparateSwatch::setRaster(TRasterP orgRas, TRasterP mainRas, + TRasterP sub1Ras, TRasterP sub2Ras, + TRasterP sub3Ras) { + //�o�� + m_sub3Swatch->setVisible(true); + m_sub3Label->setVisible(true); + + if (!isVisible()) { + m_orgRaster = TRasterP(); + m_mainRaster = TRasterP(); + m_sub1Raster = TRasterP(); + m_sub2Raster = TRasterP(); + m_sub3Raster = TRasterP(); + return; + } + + // obtain the size here in order to avoid the image shrink + // when activating / deactivating sub3 color. + qApp->processEvents(); + QSize s = m_orgSwatch->size(); + m_lx = s.width(); + m_ly = s.height(); + + m_orgRaster = orgRas; + m_mainRaster = mainRas; + m_sub1Raster = sub1Ras; + m_sub2Raster = sub2Ras; + m_sub3Raster = sub3Ras; + + m_orgSwatch->updateRaster(); + m_mainSwatch->updateRaster(); + m_sub1Swatch->updateRaster(); + m_sub2Swatch->updateRaster(); + m_sub3Swatch->updateRaster(); +} + +//-------------------------------------------------------------------- + +void SeparateSwatch::updateSeparated() { + m_mainSwatch->updateRaster(); + m_sub1Swatch->updateRaster(); + m_sub2Swatch->updateRaster(); + m_sub3Swatch->updateRaster(); +} + +//-------------------------------------------------------------------- + +void SeparateSwatch::setTranspColors(TPixel32 &mainCol, TPixel32 &sub1Col, + TPixel32 &sub2Col, TPixel32 &sub3Col, + bool showAlpha) { + m_mainSwatch->setTranspColor(mainCol, showAlpha); + m_sub1Swatch->setTranspColor(sub1Col, showAlpha); + m_sub2Swatch->setTranspColor(sub2Col, showAlpha); + m_sub3Swatch->setTranspColor(sub3Col, showAlpha); +} + +//-------------------------------------------------------------------- +void SeparateSwatch::showSub3Swatch(bool on) { + setRaster(TRasterP(), TRasterP(), TRasterP(), TRasterP(), TRasterP()); + + m_sub3Swatch->setVisible(on); + m_sub3Label->setVisible(on); +} \ No newline at end of file diff --git a/toonz/sources/toonz/separatecolorsswatch.h b/toonz/sources/toonz/separatecolorsswatch.h new file mode 100644 index 0000000..b25fc8d --- /dev/null +++ b/toonz/sources/toonz/separatecolorsswatch.h @@ -0,0 +1,86 @@ +#pragma once +#ifndef SEPARATECOLORSSWATCH_H +#define SEPARATECOLORSSWATCH_H + +#include "traster.h" +#include + +class QAction; +class QLabel; + +enum SWATCH_TYPE { Original, Main, Sub1, Sub2, Sub3 }; + +class SeparateSwatch; + +class SeparateSwatchArea : public QWidget { + Q_OBJECT + QPoint m_pos; + SWATCH_TYPE m_type; + bool m_panning; + SeparateSwatch* m_sw; + TRaster32P m_r; + TAffine getFinalAff(); + + TPixel32 m_transparentColor; + +public: + SeparateSwatchArea(SeparateSwatch* parent, SWATCH_TYPE type); + void updateRaster(bool dragging = false); + void updateSeparated(bool dragging = false); + void setTranspColor(TPixel32& lineColor, bool showAlpha = false); + +protected: + void paintEvent(QPaintEvent* event); + void mousePressEvent(QMouseEvent* event); + void mouseMoveEvent(QMouseEvent* event); + void mouseReleaseEvent(QMouseEvent* event); + void wheelEvent(QWheelEvent* event); + void keyPressEvent(QKeyEvent* event); + // focus on mouse enter + void enterEvent(QEvent* event); +}; + +class SeparateSwatch : public QWidget { + Q_OBJECT + +public: + int m_lx, m_ly; + bool m_enabled; + SeparateSwatchArea* m_orgSwatch; + SeparateSwatchArea* m_mainSwatch; + SeparateSwatchArea* m_sub1Swatch; + SeparateSwatchArea* m_sub2Swatch; + SeparateSwatchArea* m_sub3Swatch; + + QLabel* m_sub3Label; + + TAffine m_viewAff; + TRasterP m_orgRaster; + TRasterP m_mainRaster; + TRasterP m_sub1Raster; + TRasterP m_sub2Raster; + TRasterP m_sub3Raster; + + SeparateSwatch(QWidget* parent, int lx, int ly); + + void setRaster(TRasterP orgRas, TRasterP mainRas, TRasterP sub1Ras, + TRasterP sub2Ras); + void setRaster(TRasterP orgRas, TRasterP mainRas, TRasterP sub1Ras, + TRasterP sub2Ras, TRasterP sub3Ras); + void updateSeparated(); + bool isEnabled(); + void enable(bool state); + void setTranspColors(TPixel32& mainCol, TPixel32& sub1Col, TPixel32& sub2Col, + TPixel32& sub3Col, bool showAlpha = false); + +protected: + void resizeEvent(QResizeEvent* event); + +protected slots: + void showSub3Swatch(bool); + +signals: + void enabled(); +}; + +#endif \ No newline at end of file