#include "CCColorizer.h" #include "math/Mat3.h" #include "2d/CCNode.h" #include "renderer/ccShaders.h" #include "renderer/backend/ProgramCache.h" #include "renderer/backend/Device.h" NS_CC_BEGIN #define FLT_SQRT_2 1.4142135623730950488016887242097 #define FLT_SQRT_3 1.7320508075688772935274463415059 #define FLT_XRS (1 / FLT_SQRT_2) #define FLT_XRC FLT_XRS #define FLT_YRS (-1 / FLT_SQRT_3) #define FLT_YRC (FLT_SQRT_2 / FLT_SQRT_3) #define RLUM (0.3086) #define GLUM (0.6094) #define BLUM (0.0820) typedef float _M3X3[3][3]; typedef float _M4X4[4][4]; static Vec3 fastest_rgb2hsv(const Color3B& rgb) { float r = rgb.r / 255.0f; float g = rgb.g / 255.0f; float b = rgb.b / 255.0f; float h, s, v; float K = 0.f; if (g < b) { std::swap(g, b); K = -1.f; } if (r < g) { std::swap(r, g); K = -2.f / 6.f - K; } float chroma = r - std::min(g, b); h = fabs(K + (g - b) / (6.f * chroma + 1e-20f)); s = chroma / (r + 1e-20f); v = r; return Vec3(h, s, v); } static Vec3 rgb2hsv(const Color3B& rgb) { unsigned char dst_h, dst_s, dst_v; float r = rgb.r / 255.0f; float g = rgb.g / 255.0f; float b = rgb.b / 255.0f; float h, s, v; // h:0-360.0, s:0.0-1.0, v:0.0-1.0 float max = std::max({ r, g, b }); float min = std::min({ r, g, b }); v = max; if (max == 0.0f) { s = 0; h = 0; } else if (max - min == 0.0f) { s = 0; h = 0; } else { s = (max - min) / max; if (max == r) { h = 60 * ((g - b) / (max - min)) + 0; } else if (max == g) { h = 60 * ((b - r) / (max - min)) + 120; } else { h = 60 * ((r - g) / (max - min)) + 240; } } if (h < 0) h += 360.0f; dst_h = (unsigned char)(h / 2); // dst_h : 0-180 dst_s = (unsigned char)(s/* * 255*/); // dst_s : 0-255 dst_v = (unsigned char)(v /** 255*/); // dst_v : 0-255 return Vec3(dst_h / 180.f, dst_s, dst_v); } static void hsv2rgb(const unsigned char& src_h, const unsigned char& src_s, const unsigned char& src_v, unsigned char& dst_r, unsigned char& dst_g, unsigned char& dst_b) { float h = src_h * 2.0f; // 0-360 float s = src_s / 255.0f; // 0.0-1.0 float v = src_v / 255.0f; // 0.0-1.0 float r, g, b; // 0.0-1.0 int hi = (int)(h / 60.0f) % 6; float f = (h / 60.0f) - hi; float p = v * (1.0f - s); float q = v * (1.0f - s * f); float t = v * (1.0f - s * (1.0f - f)); switch (hi) { case 0: r = v, g = t, b = p; break; case 1: r = q, g = v, b = p; break; case 2: r = p, g = v, b = t; break; case 3: r = p, g = q, b = v; break; case 4: r = t, g = p, b = v; break; case 5: r = v, g = p, b = q; break; } dst_r = (unsigned char)(r * 255); // dst_r : 0-255 dst_g = (unsigned char)(g * 255); // dst_r : 0-255 dst_b = (unsigned char)(b * 255); // dst_r : 0-255 } static void printmat3(float mat[3][3]) { int x, y; char svalue[256]; int n = 0; for (y = 0; y < 3; y++) { for (x = 0; x < 3; x++) n += sprintf(svalue + n, "%f ", mat[y][x]); n += sprintf(svalue + n, "\n"); } n += sprintf(svalue + n, "\n"); log("%s", svalue); } static void printmat4(float mat[4][4]) { int x, y; char svalue[256]; int n = 0; for (y = 0; y < 4; y++) { for (x = 0; x < 4; x++) n += sprintf(svalue + n, "%f ", mat[y][x]); n += sprintf(svalue + n, "\n"); } n += sprintf(svalue + n, "\n"); log("%s", svalue); } inline void printmat3(float(&mat)[9]) { printmat3((_M3X3&)mat); } inline void printmat4(float(&mat)[16]) { printmat4((_M4X4&)mat); } inline void createRotationX(Mat4* dst, float sv, float cv) { dst->m[5] = cv; dst->m[6] = sv; dst->m[9] = -sv; dst->m[10] = cv; } inline void createRotationY(Mat4* dst, float sv, float cv) { dst->m[0] = cv; dst->m[2] = -sv; dst->m[8] = sv; dst->m[10] = cv; } inline void createRotationZ(Mat4* dst, float sv, float cv) { dst->m[0] = cv; dst->m[1] = sv; dst->m[4] = -sv; dst->m[5] = cv; } inline void rotateX(Mat4* dst, float sv, float cv) { Mat4 temp; createRotationX(&temp, sv, cv); Mat4::multiply(temp, *dst, dst); } inline void rotateY(Mat4* dst, float sv, float cv) { Mat4 temp; createRotationY(&temp, sv, cv); Mat4::multiply(temp, *dst, dst); // dst->multiply(temp); pitfall: matrix1 * matrix2 != matrix2 * matrix1 } inline void rotateZ(Mat4* dst, float sv, float cv) { Mat4 temp; createRotationZ(&temp, sv, cv); Mat4::multiply(temp, *dst, dst); } inline void rotateZ(Mat4* dst, float angle) { Mat4 temp; Mat4::createRotationZ(angle, &temp); Mat4::multiply(temp, *dst, dst); } /* * matrixmult - * multiply two matricies */ static void matrixmult(float a[4][4], float b[4][4], float c[4][4]) { int x, y; float temp[4][4]; for (y = 0; y < 4; y++) for (x = 0; x < 4; x++) { temp[y][x] = b[y][0] * a[0][x] + b[y][1] * a[1][x] + b[y][2] * a[2][x] + b[y][3] * a[3][x]; } for (y = 0; y < 4; y++) for (x = 0; x < 4; x++) c[y][x] = temp[y][x]; } /* * identmat - * make an identity matrix */ inline void identmat(float* matrix) { *matrix++ = 1.0; /* row 1 */ *matrix++ = 0.0; *matrix++ = 0.0; *matrix++ = 0.0; *matrix++ = 0.0; /* row 2 */ *matrix++ = 1.0; *matrix++ = 0.0; *matrix++ = 0.0; *matrix++ = 0.0; /* row 3 */ *matrix++ = 0.0; *matrix++ = 1.0; *matrix++ = 0.0; *matrix++ = 0.0; /* row 4 */ *matrix++ = 0.0; *matrix++ = 0.0; *matrix++ = 1.0; } /* * xformpnt - * transform a 3D point using a matrix */ static void xformpnt(float matrix[4][4], float x, float y, float z, float* tx, float* ty, float* tz) { *tx = x * matrix[0][0] + y * matrix[1][0] + z * matrix[2][0] + matrix[3][0]; *ty = x * matrix[0][1] + y * matrix[1][1] + z * matrix[2][1] + matrix[3][1]; *tz = x * matrix[0][2] + y * matrix[1][2] + z * matrix[2][2] + matrix[3][2]; } /* * cscalemat - * make a color scale marix */ static void cscalemat(float mat[4][4], float rscale, float gscale, float bscale) { float mmat[4][4]; mmat[0][0] = rscale; mmat[0][1] = 0.0; mmat[0][2] = 0.0; mmat[0][3] = 0.0; mmat[1][0] = 0.0; mmat[1][1] = gscale; mmat[1][2] = 0.0; mmat[1][3] = 0.0; mmat[2][0] = 0.0; mmat[2][1] = 0.0; mmat[2][2] = bscale; mmat[2][3] = 0.0; mmat[3][0] = 0.0; mmat[3][1] = 0.0; mmat[3][2] = 0.0; mmat[3][3] = 1.0; matrixmult(mmat, mat, mat); } /* * lummat - * make a luminance marix */ static void lummat(float mat[4][4]) { float mmat[4][4]; float rwgt, gwgt, bwgt; rwgt = RLUM; gwgt = GLUM; bwgt = BLUM; mmat[0][0] = rwgt; mmat[0][1] = rwgt; mmat[0][2] = rwgt; mmat[0][3] = 0.0; mmat[1][0] = gwgt; mmat[1][1] = gwgt; mmat[1][2] = gwgt; mmat[1][3] = 0.0; mmat[2][0] = bwgt; mmat[2][1] = bwgt; mmat[2][2] = bwgt; mmat[2][3] = 0.0; mmat[3][0] = 0.0; mmat[3][1] = 0.0; mmat[3][2] = 0.0; mmat[3][3] = 1.0; matrixmult(mmat, mat, mat); } /* * saturatemat - * make a saturation marix */ static void saturatemat(float mat[4][4], float sat) { float mmat[4][4]; float a, b, c, d, e, f, g, h, i; float rwgt, gwgt, bwgt; rwgt = RLUM; gwgt = GLUM; bwgt = BLUM; a = (1.0 - sat) * rwgt + sat; b = (1.0 - sat) * rwgt; c = (1.0 - sat) * rwgt; d = (1.0 - sat) * gwgt; e = (1.0 - sat) * gwgt + sat; f = (1.0 - sat) * gwgt; g = (1.0 - sat) * bwgt; h = (1.0 - sat) * bwgt; i = (1.0 - sat) * bwgt + sat; mmat[0][0] = a; mmat[0][1] = b; mmat[0][2] = c; mmat[0][3] = 0.0; mmat[1][0] = d; mmat[1][1] = e; mmat[1][2] = f; mmat[1][3] = 0.0; mmat[2][0] = g; mmat[2][1] = h; mmat[2][2] = i; mmat[2][3] = 0.0; mmat[3][0] = 0.0; mmat[3][1] = 0.0; mmat[3][2] = 0.0; mmat[3][3] = 1.0; matrixmult(mmat, mat, mat); } /* * offsetmat - * offset r, g, and b */ static void offsetmat(float mat[4][4], float roffset, float goffset, float boffset) { float mmat[4][4]; mmat[0][0] = 1.0; mmat[0][1] = 0.0; mmat[0][2] = 0.0; mmat[0][3] = 0.0; mmat[1][0] = 0.0; mmat[1][1] = 1.0; mmat[1][2] = 0.0; mmat[1][3] = 0.0; mmat[2][0] = 0.0; mmat[2][1] = 0.0; mmat[2][2] = 1.0; mmat[2][3] = 0.0; mmat[3][0] = roffset; mmat[3][1] = goffset; mmat[3][2] = boffset; mmat[3][3] = 1.0; matrixmult(mmat, mat, mat); } /* * xrotate - * rotate about the x (red) axis */ inline void xrotatemat(float mat[4][4], float rs, float rc) { float mmat[4][4]; mmat[0][0] = 1.0; mmat[0][1] = 0.0; mmat[0][2] = 0.0; mmat[0][3] = 0.0; mmat[1][0] = 0.0; mmat[1][1] = rc; mmat[1][2] = rs; mmat[1][3] = 0.0; mmat[2][0] = 0.0; mmat[2][1] = -rs; mmat[2][2] = rc; mmat[2][3] = 0.0; mmat[3][0] = 0.0; mmat[3][1] = 0.0; mmat[3][2] = 0.0; mmat[3][3] = 1.0; matrixmult(mmat, mat, mat); } /* * yrotate - * rotate about the y (green) axis */ static void yrotatemat(float mat[4][4], float rs, float rc) { float mmat[4][4]; mmat[0][0] = rc; mmat[0][1] = 0.0; mmat[0][2] = -rs; mmat[0][3] = 0.0; mmat[1][0] = 0.0; mmat[1][1] = 1.0; mmat[1][2] = 0.0; mmat[1][3] = 0.0; mmat[2][0] = rs; mmat[2][1] = 0.0; mmat[2][2] = rc; mmat[2][3] = 0.0; mmat[3][0] = 0.0; mmat[3][1] = 0.0; mmat[3][2] = 0.0; mmat[3][3] = 1.0; matrixmult(mmat, mat, mat); } /* * zrotate - * rotate about the z (blue) axis */ static void zrotatemat(float mat[4][4], float rs, float rc) { float mmat[4][4]; mmat[0][0] = rc; mmat[0][1] = rs; mmat[0][2] = 0.0; mmat[0][3] = 0.0; mmat[1][0] = -rs; mmat[1][1] = rc; mmat[1][2] = 0.0; mmat[1][3] = 0.0; mmat[2][0] = 0.0; mmat[2][1] = 0.0; mmat[2][2] = 1.0; mmat[2][3] = 0.0; mmat[3][0] = 0.0; mmat[3][1] = 0.0; mmat[3][2] = 0.0; mmat[3][3] = 1.0; matrixmult(mmat, mat, mat); } /* * zshear - * shear z using x and y. */ static void zshearmat(float mat[4][4], float dx, float dy) { float mmat[4][4]; mmat[0][0] = 1.0; mmat[0][1] = 0.0; mmat[0][2] = dx; mmat[0][3] = 0.0; mmat[1][0] = 0.0; mmat[1][1] = 1.0; mmat[1][2] = dy; mmat[1][3] = 0.0; mmat[2][0] = 0.0; mmat[2][1] = 0.0; mmat[2][2] = 1.0; mmat[2][3] = 0.0; mmat[3][0] = 0.0; mmat[3][1] = 0.0; mmat[3][2] = 0.0; mmat[3][3] = 1.0; matrixmult(mmat, mat, mat); } /* * simplehuerotatemat - * simple hue rotation. This changes luminance */ static void simplehuerotatemat(float mat[4][4], float rot) { // log("************* simplehuerotatemat"); float mag; float xrs, xrc; float yrs, yrc; float zrs, zrc; identmat((float*)mat); /* rotate the grey vector into positive Z */ mag = sqrt(2.0); xrs = 1.0 / mag; xrc = 1.0 / mag; xrotatemat(mat, xrs, xrc); //printmat(mat); mag = sqrt(3.0); yrs = -1.0 / mag; yrc = sqrt(2.0) / mag; yrotatemat(mat, yrs, yrc); //printmat(mat); /* rotate the hue */ zrs = sin(rot); zrc = cos(rot); zrotatemat(mat, zrs, zrc); //printmat(mat); /* rotate the grey vector back into place */ yrotatemat(mat, -yrs, yrc); // printmat(mat); xrotatemat(mat, -xrs, xrc); //printmat(mat); // log("############## simplehuerotatemat"); } /* * huerotatemat - * rotate the hue, while maintaining luminance. */ static void huerotatemat(float mat[4][4], float rot) { float mmat[4][4]; float mag; float lx, ly, lz; float xrs, xrc; float yrs, yrc; float zrs, zrc; float zsx, zsy; identmat((float*)mat); identmat((float*)&mmat); /* rotate the grey vector into positive Z */ mag = sqrt(2.0); xrs = 1.0 / mag; xrc = 1.0 / mag; xrotatemat(mmat, xrs, xrc); mag = sqrt(3.0); yrs = -1.0 / mag; yrc = sqrt(2.0) / mag; yrotatemat(mmat, yrs, yrc); /* shear the space to make the luminance plane horizontal */ xformpnt(mmat, RLUM, GLUM, BLUM, &lx, &ly, &lz); zsx = lx / lz; zsy = ly / lz; zshearmat(mmat, zsx, zsy); /* rotate the hue */ zrs = sin(rot); zrc = cos(rot); zrotatemat(mmat, zrs, zrc); /* unshear the space to put the luminance plane back */ zshearmat(mmat, -zsx, -zsy); /* rotate the grey vector back into place */ yrotatemat(mmat, -yrs, yrc); xrotatemat(mmat, -xrs, xrc); matrixmult(mmat, mat, mat); } /* * xformpnt - * transform a 3D point using a matrix */ static void xformpnt(Mat3* matrix, float x, float y, float z, float* tx, float* ty, float* tz) { *tx = x * matrix->m[0] + y * matrix->m[3] + z * matrix->m[6]; *ty = x * matrix->m[1] + y * matrix->m[4] + z * matrix->m[7]; *tz = x * matrix->m[2] + y * matrix->m[5] + z * matrix->m[8]; } /* * zshear - * shear z using x and y. */ static void zshearmat(Mat3* mat, float dx, float dy) { // float mmat[4][4]; Mat3 mmat; //mmat[0][0] = 1.0; //mmat[0][1] = 0.0; mmat.m[2] = dx; //mmat[0][3] = 0.0; //mmat[1][0] = 0.0; //mmat[1][1] = 1.0; mmat.m[5] = dy; //mmat[1][3] = 0.0; //mmat[2][0] = 0.0; //mmat[2][1] = 0.0; //mmat[2][2] = 1.0; //mmat[2][3] = 0.0; //mmat[3][0] = 0.0; //mmat[3][1] = 0.0; //mmat[3][2] = 0.0; //mmat[3][3] = 1.0; mat->mult(mmat);//matrixmult(mmat, mat, mat); } static void createHueMatrix(Mat4* dst, float angle) { // log("************* createHueMatrix4f"); memcpy(dst, &dst->IDENTITY, sizeof(*dst)); /* rotate the grey vector into positive Z */ createRotationX(dst, FLT_XRS, FLT_XRC);// rotateX(dst, FLT_XRS, FLT_XRC); rotateY(dst, FLT_YRS, FLT_YRC); /* rotate the hue */ rotateZ(dst, angle); /* rotate the grey vector back into place */ rotateY(dst, -FLT_YRS, FLT_YRC); rotateX(dst, -FLT_XRS, FLT_XRC); // log("############## createHueMatrix4f"); } static void setMatrixHueOptimized(Mat3* dst, float angle) { float lx, ly, lz; float zsx, zsy; // memcpy(dst, &dst->IDENTITY, sizeof(*dst)); /* rotate the grey vector into positive Z */ dst->createRotationX(FLT_XRS, FLT_XRC);// rotateX(dst, FLT_XRS, FLT_XRC); dst->rotateY(FLT_YRS, FLT_YRC); /* shear the space to make the luminance plane horizontal */ xformpnt(dst, RLUM, GLUM, BLUM, &lx, &ly, &lz); zsx = lx / lz; zsy = ly / lz; zshearmat(dst, zsx, zsy); /* rotate the hue */ dst->rotateZ(angle); /* unshear the space to put the luminance plane back */ zshearmat(dst, -zsx, -zsy); /* rotate the grey vector back into place */ dst->rotateY(-FLT_YRS, FLT_YRC); dst->rotateX(-FLT_XRS, FLT_XRC); } /* * Hue - * HSV - H */ static void setMatrixHue(Mat3* dst, float angle) { log("************* createHueMat3"); // memcpy(dst, &dst->IDENTITY, sizeof(*dst)); /* rotate the grey vector into positive Z */ dst->createRotationX(FLT_XRS, FLT_XRC);// rotateX(dst, FLT_XRS, FLT_XRC); dst->rotateY(FLT_YRS, FLT_YRC); /* rotate the hue */ dst->rotateZ(angle); /* rotate the grey vector back into place */ dst->rotateY(-FLT_YRS, FLT_YRC); dst->rotateX(-FLT_XRS, FLT_XRC); log("############## createHueMat3"); } /* * saturatemat - 0~1, 0%~100% ? * HSV - S */ static void setMatrixSat(Mat3* dst, float sat) { Mat3 temp; float a, b, c, d, e, f, g, h, i; float rwgt, gwgt, bwgt; rwgt = RLUM; gwgt = GLUM; bwgt = BLUM; a = (1.0 - sat) * rwgt + sat; b = (1.0 - sat) * rwgt; c = (1.0 - sat) * rwgt; d = (1.0 - sat) * gwgt; e = (1.0 - sat) * gwgt + sat; f = (1.0 - sat) * gwgt; g = (1.0 - sat) * bwgt; h = (1.0 - sat) * bwgt; i = (1.0 - sat) * bwgt + sat; temp.m[0] = a; temp.m[1] = b; temp.m[2] = c; // mmat[0][3] = 0.0; temp.m[3] = d; temp.m[4] = e; temp.m[5] = f; //mmat[1][3] = 0.0; temp.m[6] = g; temp.m[7] = h; temp.m[8] = i; // mmat[2][3] = 0.0; //mmat[3][0] = 0.0; //mmat[3][1] = 0.0; //mmat[3][2] = 0.0; //mmat[3][3] = 1.0; // matrixmult(mmat, mat, mat); dst->mult(temp); } /* Value * HSV - V: 0~1, 0%, 100% */ static void setMatrixVal(Mat3* dst, float value) { // float mmat[4][4]; Mat3 temp; temp.m[0] = value; //temp.m[1] = 0.0; //temp.m[2] = 0.0; //mmat[0][3] = 0.0; //mmat[1][0] = 0.0; temp.m[4] = value; // mmat[1][2] = 0.0; //mmat[1][3] = 0.0; //mmat[2][0] = 0.0; //mmat[2][1] = 0.0; temp.m[8] = value; //mmat[2][3] = 0.0; dst->mult(temp); // matrixmult(mmat, mat, mat); } bool Colorizer::enableNodeIntelliShading(Node* node, bool noMVP, const Vec3& hsv, const Vec3& filter, bool forceShading, const Vec3& hsvShading) { if (node == nullptr) return false; node->setProgramState(backend::ProgramType::HSV); updateNodeHsv(node, hsv, filter, forceShading, hsvShading); return true; } void Colorizer::updateNodeHsv(Node* node, const Vec3& hsv, const Vec3& filter, bool forceShading, const Vec3& hsvShading) { Mat3 hsvMatrix; setMatrixHueOptimized(&hsvMatrix, hsv.x); setMatrixSat(&hsvMatrix, hsv.y); setMatrixVal(&hsvMatrix, hsv.z); printmat3(hsvMatrix.m); auto programState = node->getProgramState(); programState->setCallbackUniform(programState->getUniformLocation("u_mix_hsv"), [hsvMatrix](backend::ProgramState* programState, const backend::UniformLocation& location) { programState->setUniform(location, &hsvMatrix.m[0], sizeof(hsvMatrix)); }); programState->setUniform(programState->getUniformLocation("u_filter_rgb"), &filter, sizeof(filter)); int32_t value = forceShading ? 1 : 0; programState->setUniform(programState->getUniformLocation("u_force_shading"), &value, sizeof(value)); } NS_CC_END