1748 lines
60 KiB
C++
1748 lines
60 KiB
C++
#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 <QPushButton>
|
|
#include <QComboBox>
|
|
#include <QLabel>
|
|
#include <QMainWindow>
|
|
#include <QCheckBox>
|
|
#include <QSplitter>
|
|
#include <QGroupBox>
|
|
#include <QMessageBox>
|
|
#include <QThreadPool>
|
|
#include <QVector2D>
|
|
#include <QPainter>
|
|
|
|
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<TFrameId>& 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<TFilePath>& 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<TFrameId> 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<TFrameId> 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<TFrameId> 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_c3 = 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<SeparateColorsPopup> openSeparateColorsPopup(
|
|
MI_SeparateColors);
|