shun-iwasawa ad7711
#include "toonzqt/lutcalibrator.h"
shun-iwasawa ad7711
shun-iwasawa ad7711
// Tnzlib includes
shun-iwasawa ad7711
#include "toonz/preferences.h"
shun-iwasawa ad7711
shun-iwasawa ad7711
// TnzCore includes
shun-iwasawa ad7711
#include "tmsgcore.h"
shun-iwasawa ad7711
shun-iwasawa ad7711
#include <qopenglshader></qopenglshader>
shun-iwasawa ad7711
#include <qopenglshaderprogram></qopenglshaderprogram>
shun-iwasawa ad7711
#include <qopenglframebufferobject></qopenglframebufferobject>
shun-iwasawa ad7711
#include <qopengltexture></qopengltexture>
shun-iwasawa ad7711
#include <qopenglcontext></qopenglcontext>
shun-iwasawa ad7711
#include <qoffscreensurface></qoffscreensurface>
shun-iwasawa ad7711
#include <qfile></qfile>
shun-iwasawa ad7711
#include <qcolor></qcolor>
shun-iwasawa ad7711
shun-iwasawa ad7711
namespace {
shun-iwasawa ad7711
inline bool execWarning(const QString& s) {
shun-iwasawa ad7711
  DVGui::MsgBox(DVGui::WARNING, s);
shun-iwasawa ad7711
  return false;
shun-iwasawa ad7711
}
shun-iwasawa e40777
};  // namespace
shun-iwasawa ad7711
shun-iwasawa ad7711
#ifdef WIN32
shun-iwasawa ad7711
shun-iwasawa ad7711
#include <qstringlist></qstringlist>
shun-iwasawa ad7711
#include <qsettings></qsettings>
shun-iwasawa ad7711
#include <qbytearray></qbytearray>
shun-iwasawa ad7711
shun-iwasawa ad7711
namespace {
shun-iwasawa ad7711
// obtain monitor information from registry
shun-iwasawa ad7711
QStringList getMonitorNames() {
shun-iwasawa ad7711
  QStringList subPathSet;
shun-iwasawa ad7711
  // QSettings regSys("SYSTEM\\CurrentControlSet\\Enum\\DISPLAY",
shun-iwasawa ad7711
  // QSettings::NativeFormat);
shun-iwasawa ad7711
  QSettings regSys(
shun-iwasawa ad7711
      "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Enum\\DISPLAY",
shun-iwasawa ad7711
      QSettings::NativeFormat);
shun-iwasawa ad7711
  QStringList children = regSys.childGroups();
shun-iwasawa ad7711
  // return value
shun-iwasawa ad7711
  QStringList nameList;
shun-iwasawa ad7711
shun-iwasawa ad7711
  if (children.isEmpty()) {
shun-iwasawa ad7711
    std::cout << "getMonitorNames : Failed to Open Registry" << std::endl;
shun-iwasawa ad7711
    return nameList;
shun-iwasawa ad7711
  }
shun-iwasawa ad7711
shun-iwasawa ad7711
  for (int c = 0; c < children.size(); c++) {
shun-iwasawa ad7711
    // Parent Key : DISPLAY
shun-iwasawa ad7711
    // Child Keys : AVO0000, ENC2174, etc.
shun-iwasawa ad7711
    // Grandchild Key : 5&388..
shun-iwasawa ad7711
    // Find grandchild key which contains a great-grandchild key named "Control"
shun-iwasawa ad7711
shun-iwasawa ad7711
    regSys.beginGroup(children.at(c));  // Child keys : AVO0000, ENC2174, etc.
shun-iwasawa ad7711
    QStringList grandChildren = regSys.childGroups();
shun-iwasawa ad7711
    for (int gc = 0; gc < grandChildren.size(); gc++) {
shun-iwasawa ad7711
      regSys.beginGroup(grandChildren.at(gc));  // Grandchild key : 5&388..
shun-iwasawa ad7711
shun-iwasawa ad7711
      QStringList greatGrandChildren = regSys.childGroups();
shun-iwasawa ad7711
shun-iwasawa ad7711
      if (greatGrandChildren.contains(
shun-iwasawa ad7711
              "Control"))  // If the key "Control" is found
shun-iwasawa ad7711
      {
shun-iwasawa ad7711
        // Obtain variable "EDID" from the key "Device Parameters"
shun-iwasawa ad7711
        regSys.beginGroup("Device Parameters");
shun-iwasawa ad7711
shun-iwasawa ad7711
        // the key may be not "EDID", but "BAD_EDID"
shun-iwasawa ad7711
        if (regSys.contains("EDID")) {
shun-iwasawa ad7711
          QString subPath = regSys.group().replace("/", "\\").prepend(
shun-iwasawa ad7711
              "SYSTEM\\CurrentControlSet\\Enum\\DISPLAY\\");
shun-iwasawa ad7711
          subPathSet.push_back(subPath);
shun-iwasawa ad7711
        }
shun-iwasawa ad7711
        regSys.endGroup();
shun-iwasawa ad7711
      }
shun-iwasawa ad7711
      regSys.endGroup();
shun-iwasawa ad7711
    }
shun-iwasawa ad7711
    // subPath may not be one...?
shun-iwasawa ad7711
    // if(!subPath.isEmpty())
shun-iwasawa ad7711
    //	break;
shun-iwasawa ad7711
    regSys.endGroup();
shun-iwasawa ad7711
  }
shun-iwasawa ad7711
shun-iwasawa ad7711
  if (subPathSet.isEmpty()) {
shun-iwasawa ad7711
    std::cout << "getMonitorNames : Failed to Find Current EDID" << std::endl;
shun-iwasawa ad7711
    return nameList;
shun-iwasawa ad7711
  }
shun-iwasawa ad7711
shun-iwasawa ad7711
  // for each subPath ( it may become more than one when using submonitor )
shun-iwasawa ad7711
  for (int sp = 0; sp < subPathSet.size(); sp++) {
shun-iwasawa ad7711
    QString subPath = subPathSet.at(sp);
shun-iwasawa ad7711
shun-iwasawa ad7711
    HKEY handle = 0;
shun-iwasawa ad7711
shun-iwasawa ad7711
    LONG res = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
shun-iwasawa ad7711
                             reinterpret_cast<const wchar_t*="">(subPath.utf16()),</const>
shun-iwasawa ad7711
                             0, KEY_READ, &handle);
shun-iwasawa ad7711
shun-iwasawa ad7711
    if (res == ERROR_SUCCESS && handle) {
shun-iwasawa ad7711
      QString keyStr("EDID");
shun-iwasawa ad7711
shun-iwasawa ad7711
      // get the size and type of the value
shun-iwasawa ad7711
      DWORD dataType;
shun-iwasawa ad7711
      DWORD dataSize;
shun-iwasawa ad7711
      LONG res = RegQueryValueExW(
shun-iwasawa ad7711
          handle, reinterpret_cast<const wchar_t*="">(keyStr.utf16()), 0,</const>
shun-iwasawa ad7711
          &dataType, 0, &dataSize);
shun-iwasawa ad7711
shun-iwasawa ad7711
      if (res != ERROR_SUCCESS) {
shun-iwasawa ad7711
        RegCloseKey(handle);
shun-iwasawa ad7711
        continue;
shun-iwasawa ad7711
      }
shun-iwasawa ad7711
shun-iwasawa ad7711
      // get the value
shun-iwasawa ad7711
      QByteArray ba(dataSize, 0);
shun-iwasawa ad7711
      res = RegQueryValueExW(
shun-iwasawa ad7711
          handle, reinterpret_cast<const wchar_t*="">(keyStr.utf16()), 0, 0,</const>
shun-iwasawa ad7711
          reinterpret_cast<unsigned char*="">(ba.data()), &dataSize);</unsigned>
shun-iwasawa ad7711
shun-iwasawa ad7711
      if (res != ERROR_SUCCESS) {
shun-iwasawa ad7711
        RegCloseKey(handle);
shun-iwasawa ad7711
        continue;
shun-iwasawa ad7711
      }
shun-iwasawa ad7711
shun-iwasawa ad7711
      QString s;
shun-iwasawa ad7711
      if (dataSize) {
shun-iwasawa ad7711
        s = QString::fromUtf16((const ushort*)ba.constData(), ba.size() / 2);
shun-iwasawa ad7711
      }
shun-iwasawa ad7711
shun-iwasawa ad7711
      QString valStr;
shun-iwasawa ad7711
      QList<int> valArray;</int>
shun-iwasawa ad7711
      for (int b = 0; b < s.length(); b++) {
shun-iwasawa ad7711
        QChar c1((int)s[b].unicode() % 256);
shun-iwasawa ad7711
        QChar c2((int)s[b].unicode() / 256);
shun-iwasawa ad7711
        valStr.append(c1.toLatin1());
shun-iwasawa ad7711
        valStr.append(c2.toLatin1());
shun-iwasawa ad7711
        valArray.append((int)s[b].unicode() % 256);
shun-iwasawa ad7711
        valArray.append((int)s[b].unicode() / 256);
shun-iwasawa ad7711
      }
shun-iwasawa ad7711
shun-iwasawa ad7711
      // machine name starts from "FC 00", end with "0A"
shun-iwasawa ad7711
      int index1 = valArray.indexOf(252);         // FC
shun-iwasawa ad7711
      int index2 = valArray.indexOf(10, index1);  // 0A
shun-iwasawa ad7711
shun-iwasawa ad7711
      if (index1 > 0 && index2 > 0) {
shun-iwasawa ad7711
        QString machineName = valStr.mid(index1 + 2, index2 - index1 - 2);
shun-iwasawa ad7711
        nameList.push_back(machineName);
shun-iwasawa ad7711
        // std::wcout << "machine name = " << machineName.toStdWString() <<
shun-iwasawa ad7711
        // std::endl;
shun-iwasawa ad7711
      }
shun-iwasawa ad7711
      RegCloseKey(handle);
shun-iwasawa ad7711
shun-iwasawa ad7711
    } else {
shun-iwasawa ad7711
      std::cout << "getMonitorNames : failed to get handle" << std::endl;
shun-iwasawa ad7711
      continue;
shun-iwasawa ad7711
    }
shun-iwasawa ad7711
  }
shun-iwasawa ad7711
shun-iwasawa ad7711
  if (nameList.isEmpty())
shun-iwasawa ad7711
    std::cout << "getMonitorNames : No Monitor Name Found" << std::endl;
shun-iwasawa ad7711
shun-iwasawa ad7711
  return nameList;
shun-iwasawa ad7711
}
shun-iwasawa e40777
};  // namespace
shun-iwasawa ad7711
#endif
shun-iwasawa ad7711
shun-iwasawa ad7711
//-----------------------------------------------------------------------------
shun-iwasawa ad7711
shun-iwasawa a8f111
LutCalibrator::LutCalibrator() {
shun-iwasawa a8f111
  LutManager::instance()->registerCalibrator(this);
shun-iwasawa a8f111
}
shun-iwasawa a8f111
shun-iwasawa a8f111
//-----------------------------------------------------------------------------
shun-iwasawa a8f111
shun-iwasawa a8f111
LutCalibrator::~LutCalibrator() {
shun-iwasawa a8f111
  LutManager::instance()->removeCalibrator(this);
shun-iwasawa a8f111
}
shun-iwasawa a8f111
shun-iwasawa a8f111
//-----------------------------------------------------------------------------
shun-iwasawa a8f111
shun-iwasawa ad7711
void LutCalibrator::initialize() {
shun-iwasawa ad7711
  initializeOpenGLFunctions();
shun-iwasawa e40777
  m_isInitialized = true;
shun-iwasawa ad7711
shun-iwasawa 388550
  if (!LutManager::instance()->isValid()) return;
shun-iwasawa ad7711
shun-iwasawa ad7711
  // create shader
shun-iwasawa ad7711
  if (!initializeLutTextureShader()) {
shun-iwasawa ad7711
    if (m_shader.program) delete m_shader.program;
shun-iwasawa ad7711
    if (m_shader.vert) delete m_shader.vert;
shun-iwasawa ad7711
    if (m_shader.frag) delete m_shader.frag;
shun-iwasawa ad7711
    return;
shun-iwasawa ad7711
  }
shun-iwasawa ad7711
  createViewerVBO();
shun-iwasawa ad7711
shun-iwasawa ad7711
  // input 3dlut data to the shader
shun-iwasawa ad7711
  assignLutTexture();
shun-iwasawa ad7711
shun-iwasawa ad7711
  m_isValid = true;
shun-iwasawa ad7711
}
shun-iwasawa ad7711
shun-iwasawa ad7711
//-----------------------------------------------------------------------------
shun-iwasawa ad7711
shun-iwasawa 388550
void LutCalibrator::cleanup() {
shun-iwasawa e40777
  m_isInitialized = false;
shun-iwasawa 388550
  if (!isValid()) return;
shun-iwasawa ad7711
  // release shader
shun-iwasawa 388550
  if (m_shader.program) {
shun-iwasawa 388550
    delete m_shader.program;
shun-iwasawa 388550
    m_shader.program = NULL;
shun-iwasawa 388550
  }
shun-iwasawa 388550
  if (m_shader.vert) {
shun-iwasawa 388550
    delete m_shader.vert;
shun-iwasawa 388550
    m_shader.vert = NULL;
shun-iwasawa 388550
  }
shun-iwasawa 388550
  if (m_shader.frag) {
shun-iwasawa 388550
    delete m_shader.frag;
shun-iwasawa 388550
    m_shader.frag = NULL;
shun-iwasawa 388550
  }
shun-iwasawa ad7711
  // release VBO
shun-iwasawa ad7711
  if (m_viewerVBO.isCreated()) m_viewerVBO.destroy();
shun-iwasawa ad7711
  // release LUT texture
shun-iwasawa 388550
  if (m_lutTex && m_lutTex->isCreated()) {
shun-iwasawa 388550
    m_lutTex->destroy();
shun-iwasawa 388550
    delete m_lutTex;
shun-iwasawa 388550
    m_lutTex = NULL;
shun-iwasawa ad7711
  }
shun-iwasawa e40777
  m_isValid = false;
shun-iwasawa ad7711
}
shun-iwasawa ad7711
shun-iwasawa ad7711
//-----------------------------------------------------------------------------
shun-iwasawa ad7711
shun-iwasawa ad7711
bool LutCalibrator::initializeLutTextureShader() {
shun-iwasawa ad7711
  m_shader.vert = new QOpenGLShader(QOpenGLShader::Vertex);
shun-iwasawa ad7711
  const char* simple_vsrc =
shun-iwasawa ad7711
      "#version 330 core\n"
shun-iwasawa ad7711
      "// Input vertex data, different for all executions of this shader.\n"
shun-iwasawa ad7711
      "layout(location = 0) in vec3 vertexPosition;\n"
shun-iwasawa ad7711
      "layout(location = 1) in vec2 texCoord;\n"
shun-iwasawa ad7711
      "// Output data ; will be interpolated for each fragment.\n"
shun-iwasawa ad7711
      "out vec2 UV;\n"
shun-iwasawa ad7711
      "// Values that stay constant for the whole mesh.\n"
shun-iwasawa ad7711
      "void main() {\n"
shun-iwasawa ad7711
      "  // Output position of the vertex, in clip space : MVP * position\n"
shun-iwasawa ad7711
      "  gl_Position = vec4(vertexPosition, 1);\n"
shun-iwasawa ad7711
      "  // UV of the vertex. No special space for this one.\n"
shun-iwasawa ad7711
      "  UV = texCoord;\n"
shun-iwasawa ad7711
      "}\n";
shun-iwasawa ad7711
  bool ret = m_shader.vert->compileSourceCode(simple_vsrc);
shun-iwasawa ad7711
  if (!ret)
shun-iwasawa ad7711
    return execWarning(
shun-iwasawa ad7711
        QObject::tr("Failed to compile m_textureShader.vert.", "gl"));
shun-iwasawa ad7711
shun-iwasawa ad7711
  m_shader.frag = new QOpenGLShader(QOpenGLShader::Fragment);
shun-iwasawa ad7711
  const char* simple_fsrc =
shun-iwasawa ad7711
      "#version 330 core \n"
shun-iwasawa ad7711
      "// Interpolated values from the vertex shaders \n"
shun-iwasawa ad7711
      "in vec2 UV; \n"
shun-iwasawa ad7711
      "// Ouput data \n"
shun-iwasawa ad7711
      "out vec4 color; \n"
shun-iwasawa ad7711
      "// Values that stay constant for the whole mesh. \n"
shun-iwasawa ad7711
      "uniform sampler2D tex; \n"
shun-iwasawa ad7711
      "uniform sampler3D lut; \n"
shun-iwasawa ad7711
      "uniform vec3 lutSize; \n"
shun-iwasawa ad7711
      "void main() { \n"
shun-iwasawa ad7711
      "  vec3 rawColor = texture(tex,UV).rgb; \n"
shun-iwasawa ad7711
      "  vec3 scale = (lutSize - 1.0) / lutSize; \n"
shun-iwasawa ad7711
      "  vec3 offset = 1.0 / (2.0 * lutSize); \n"
shun-iwasawa ad7711
      "  color = vec4(texture(lut, scale * rawColor + offset).rgb, 1.0); \n"
shun-iwasawa ad7711
      "} \n";
shun-iwasawa ad7711
  ret = m_shader.frag->compileSourceCode(simple_fsrc);
shun-iwasawa ad7711
  if (!ret)
shun-iwasawa ad7711
    return execWarning(QObject::tr("Failed to compile m_shader.frag.", "gl"));
shun-iwasawa ad7711
shun-iwasawa ad7711
  m_shader.program = new QOpenGLShaderProgram();
shun-iwasawa ad7711
  // add shaders
shun-iwasawa ad7711
  ret = m_shader.program->addShader(m_shader.vert);
shun-iwasawa ad7711
  if (!ret)
shun-iwasawa ad7711
    return execWarning(QObject::tr("Failed to add m_shader.vert.", "gl"));
shun-iwasawa ad7711
  ret = m_shader.program->addShader(m_shader.frag);
shun-iwasawa ad7711
  if (!ret)
shun-iwasawa ad7711
    return execWarning(QObject::tr("Failed to add m_shader.frag.", "gl"));
shun-iwasawa ad7711
  // link shaders
shun-iwasawa ad7711
  ret = m_shader.program->link();
shun-iwasawa ad7711
  if (!ret)
shun-iwasawa ad7711
    return execWarning(QObject::tr("Failed to link simple shader: %1", "gl")
shun-iwasawa ad7711
                           .arg(m_shader.program->log()));
shun-iwasawa ad7711
  // obtain parameter locations
shun-iwasawa ad7711
  m_shader.vertexAttrib = m_shader.program->attributeLocation("vertexPosition");
shun-iwasawa ad7711
  if (m_shader.vertexAttrib == -1)
shun-iwasawa ad7711
    return execWarning(
shun-iwasawa ad7711
        QObject::tr("Failed to get attribute location of %1", "gl")
shun-iwasawa ad7711
            .arg("vertexPosition"));
shun-iwasawa ad7711
  m_shader.texCoordAttrib = m_shader.program->attributeLocation("texCoord");
shun-iwasawa ad7711
  if (m_shader.texCoordAttrib == -1)
shun-iwasawa ad7711
    return execWarning(
shun-iwasawa ad7711
        QObject::tr("Failed to get attribute location of %1", "gl")
shun-iwasawa ad7711
            .arg("texCoord"));
shun-iwasawa ad7711
  m_shader.texUniform = m_shader.program->uniformLocation("tex");
shun-iwasawa ad7711
  if (m_shader.texUniform == -1)
shun-iwasawa ad7711
    return execWarning(
shun-iwasawa ad7711
        QObject::tr("Failed to get uniform location of %1", "gl").arg("tex"));
shun-iwasawa ad7711
  m_shader.lutUniform = m_shader.program->uniformLocation("lut");
shun-iwasawa ad7711
  if (m_shader.lutUniform == -1)
shun-iwasawa ad7711
    return execWarning(
shun-iwasawa ad7711
        QObject::tr("Failed to get uniform location of %1", "gl").arg("lut"));
shun-iwasawa ad7711
  m_shader.lutSizeUniform = m_shader.program->uniformLocation("lutSize");
shun-iwasawa ad7711
  if (m_shader.lutSizeUniform == -1)
shun-iwasawa ad7711
    return execWarning(QObject::tr("Failed to get uniform location of %1", "gl")
shun-iwasawa ad7711
                           .arg("lutSize"));
shun-iwasawa ad7711
shun-iwasawa ad7711
  return true;
shun-iwasawa ad7711
}
shun-iwasawa ad7711
shun-iwasawa ad7711
//-----------------------------------------------------------------------------
shun-iwasawa ad7711
shun-iwasawa ad7711
void LutCalibrator::createViewerVBO() {
shun-iwasawa ad7711
  GLfloat vertex[]   = {-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f};
shun-iwasawa ad7711
  GLfloat texCoord[] = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
shun-iwasawa ad7711
shun-iwasawa ad7711
  m_viewerVBO.create();
shun-iwasawa ad7711
  m_viewerVBO.bind();
shun-iwasawa ad7711
  m_viewerVBO.allocate(4 * 4 * sizeof(GLfloat));
shun-iwasawa ad7711
  m_viewerVBO.write(0, vertex, sizeof(vertex));
shun-iwasawa ad7711
  m_viewerVBO.write(sizeof(vertex), texCoord, sizeof(texCoord));
shun-iwasawa ad7711
  m_viewerVBO.release();
shun-iwasawa ad7711
}
shun-iwasawa ad7711
shun-iwasawa ad7711
//-----------------------------------------------------------------------------
shun-iwasawa ad7711
shun-iwasawa ad7711
void LutCalibrator::onEndDraw(QOpenGLFramebufferObject* fbo) {
shun-iwasawa ad7711
  assert((glGetError()) == GL_NO_ERROR);
shun-iwasawa ad7711
  fbo->release();
shun-iwasawa ad7711
  GLuint textureId = fbo->texture();
shun-iwasawa ad7711
shun-iwasawa ad7711
  glEnable(GL_TEXTURE_2D);
shun-iwasawa e140c2
  glActiveTexture(GL_TEXTURE1);
shun-iwasawa ad7711
  glBindTexture(GL_TEXTURE_2D, textureId);
shun-iwasawa ad7711
shun-iwasawa e140c2
  glActiveTexture(GL_TEXTURE2);
shun-iwasawa 388550
  m_lutTex->bind();
shun-iwasawa ad7711
shun-iwasawa ad7711
  glPushMatrix();
shun-iwasawa ad7711
  glLoadIdentity();
shun-iwasawa ad7711
shun-iwasawa ad7711
  m_shader.program->bind();
shun-iwasawa ad7711
  m_shader.program->setUniformValue(m_shader.texUniform,
shun-iwasawa e140c2
                                    1);  // use texture unit 1
shun-iwasawa ad7711
  m_shader.program->setUniformValue(m_shader.lutUniform,
shun-iwasawa e140c2
                                    2);  // use texture unit 2
shun-iwasawa 388550
  GLfloat size = (GLfloat)LutManager::instance()->meshSize();
shun-iwasawa ad7711
  m_shader.program->setUniformValue(m_shader.lutSizeUniform, size, size, size);
shun-iwasawa ad7711
shun-iwasawa ad7711
  m_shader.program->enableAttributeArray(m_shader.vertexAttrib);
shun-iwasawa ad7711
  m_shader.program->enableAttributeArray(m_shader.texCoordAttrib);
shun-iwasawa ad7711
shun-iwasawa ad7711
  m_viewerVBO.bind();
shun-iwasawa ad7711
  m_shader.program->setAttributeBuffer(m_shader.vertexAttrib, GL_FLOAT, 0, 2);
shun-iwasawa ad7711
  m_shader.program->setAttributeBuffer(m_shader.texCoordAttrib, GL_FLOAT,
shun-iwasawa ad7711
                                       4 * 2 * sizeof(GLfloat), 2);
shun-iwasawa ad7711
  m_viewerVBO.release();
shun-iwasawa ad7711
shun-iwasawa ad7711
  glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
shun-iwasawa ad7711
shun-iwasawa ad7711
  m_shader.program->disableAttributeArray(m_shader.vertexAttrib);
shun-iwasawa ad7711
  m_shader.program->disableAttributeArray(m_shader.texCoordAttrib);
shun-iwasawa ad7711
shun-iwasawa ad7711
  m_shader.program->release();
shun-iwasawa ad7711
shun-iwasawa ad7711
  glPopMatrix();
shun-iwasawa e140c2
shun-iwasawa e140c2
  glActiveTexture(GL_TEXTURE0);  // reset the active texture unit to 0
shun-iwasawa ad7711
  glDisable(GL_TEXTURE_2D);
shun-iwasawa ad7711
shun-iwasawa ad7711
  assert((glGetError()) == GL_NO_ERROR);
shun-iwasawa ad7711
}
shun-iwasawa ad7711
shun-iwasawa ad7711
//-----------------------------------------------------------------------------
shun-iwasawa ad7711
shun-iwasawa 388550
void LutCalibrator::assignLutTexture() {
shun-iwasawa 388550
  assert(glGetError() == GL_NO_ERROR);
shun-iwasawa a8f111
  if (m_lutTex) delete m_lutTex;
shun-iwasawa 388550
  int meshSize = LutManager::instance()->meshSize();
shun-iwasawa 388550
  m_lutTex     = new QOpenGLTexture(QOpenGLTexture::Target3D);
shun-iwasawa 388550
  m_lutTex->setSize(meshSize, meshSize, meshSize);
shun-iwasawa 388550
  m_lutTex->setFormat(QOpenGLTexture::RGB32F);
shun-iwasawa 388550
  // m_lutTex->setLayers(1);
shun-iwasawa 388550
  m_lutTex->setMipLevels(1);
shun-iwasawa 388550
  m_lutTex->allocateStorage();
shun-iwasawa 388550
  m_lutTex->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
shun-iwasawa 388550
  m_lutTex->setWrapMode(QOpenGLTexture::ClampToEdge);
shun-iwasawa 388550
shun-iwasawa 388550
  m_lutTex->setData(QOpenGLTexture::RGB, QOpenGLTexture::Float32,
shun-iwasawa 388550
                    LutManager::instance()->data());
shun-iwasawa 388550
shun-iwasawa 388550
  assert(glGetError() == GL_NO_ERROR);
shun-iwasawa 388550
}
shun-iwasawa 388550
shun-iwasawa a8f111
//-----------------------------------------------------------------------------
shun-iwasawa a8f111
shun-iwasawa a8f111
void LutCalibrator::update(bool textureChanged) {
shun-iwasawa a8f111
  m_isValid = LutManager::instance()->isValid();
shun-iwasawa a8f111
  if (textureChanged) assignLutTexture();
shun-iwasawa a8f111
}
shun-iwasawa a8f111
shun-iwasawa 388550
//=============================================================================
shun-iwasawa 388550
shun-iwasawa 388550
LutManager* LutManager::instance() {
shun-iwasawa 388550
  static LutManager _instance;
shun-iwasawa 388550
  return &_instance;
shun-iwasawa 388550
}
shun-iwasawa 388550
shun-iwasawa 388550
//-----------------------------------------------------------------------------
shun-iwasawa 388550
shun-iwasawa a8f111
LutManager::LutManager() : m_isValid(false), m_currentLutPath() {
shun-iwasawa 388550
  // check whether preference enables color calibration
shun-iwasawa 388550
  if (!Preferences::instance()->isColorCalibrationEnabled()) return;
shun-iwasawa 388550
shun-iwasawa 388550
  // obtain current monitor name
shun-iwasawa 388550
  QString monitorName = getMonitorName();
shun-iwasawa 388550
shun-iwasawa 388550
  // obtain 3dlut path associated to the monitor name
shun-iwasawa 388550
  QString lutPath =
shun-iwasawa 388550
      Preferences::instance()->getColorCalibrationLutPath(monitorName);
shun-iwasawa 388550
shun-iwasawa 388550
  if (lutPath.isEmpty()) return;
shun-iwasawa 388550
shun-iwasawa 388550
  // check existence of the 3dlut file
shun-iwasawa 388550
  // load 3dlut data
shun-iwasawa 388550
  if (!loadLutFile(lutPath)) return;
shun-iwasawa a8f111
  m_currentLutPath = lutPath;
shun-iwasawa a8f111
  m_isValid        = true;
shun-iwasawa 388550
}
shun-iwasawa 388550
shun-iwasawa 388550
//-----------------------------------------------------------------------------
shun-iwasawa 388550
shun-iwasawa 388550
LutManager::~LutManager() {
shun-iwasawa 388550
  if (m_lut.data) delete[] m_lut.data;
shun-iwasawa 388550
}
shun-iwasawa 388550
shun-iwasawa 388550
//-----------------------------------------------------------------------------
shun-iwasawa 388550
shun-iwasawa 388550
QString& LutManager::getMonitorName() const {
shun-iwasawa ad7711
  static QString monitorName;
shun-iwasawa ad7711
  if (!monitorName.isEmpty()) return monitorName;
shun-iwasawa ad7711
shun-iwasawa ad7711
#ifdef WIN32
shun-iwasawa ad7711
  QStringList list = getMonitorNames();
shun-iwasawa ad7711
  if (list.isEmpty())
shun-iwasawa ad7711
    monitorName = "Any Monitor";  // this should not be translated
shun-iwasawa ad7711
  else
shun-iwasawa ad7711
    monitorName = list.at(0);  // for now only the first monitor is handled
shun-iwasawa ad7711
#else
shun-iwasawa ad7711
  monitorName = "Any Monitor";  // this should not be translated
shun-iwasawa ad7711
#endif
shun-iwasawa ad7711
shun-iwasawa ad7711
  return monitorName;
shun-iwasawa ad7711
}
shun-iwasawa ad7711
shun-iwasawa ad7711
//-----------------------------------------------------------------------------
shun-iwasawa ad7711
shun-iwasawa 388550
bool LutManager::loadLutFile(const QString& fp) {
shun-iwasawa ad7711
  struct locals {
shun-iwasawa ad7711
    // skip empty or comment lines
shun-iwasawa ad7711
    static inline QString readDataLine(QTextStream& stream) {
shun-iwasawa ad7711
      while (1) {
shun-iwasawa ad7711
        if (stream.atEnd()) return QString();
shun-iwasawa ad7711
        QString ret = stream.readLine();
shun-iwasawa ad7711
        if (!ret.isEmpty() && ret[0] != QChar('#')) return ret;
shun-iwasawa ad7711
      }
shun-iwasawa ad7711
    }
shun-iwasawa ad7711
shun-iwasawa ad7711
    static inline int lutCoords(int r, int g, int b, int meshSize) {
shun-iwasawa ad7711
      return b * meshSize * meshSize * 3 + g * meshSize * 3 + r * 3;
shun-iwasawa ad7711
    }
shun-iwasawa ad7711
  };
shun-iwasawa ad7711
shun-iwasawa ad7711
  QFile file(fp);
shun-iwasawa ad7711
  if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
shun-iwasawa ad7711
    return execWarning(QObject::tr("Failed to Open 3DLUT File."));
shun-iwasawa ad7711
shun-iwasawa ad7711
  QTextStream stream(&file);
shun-iwasawa ad7711
  QString line;
shun-iwasawa ad7711
shun-iwasawa ad7711
  //---- read the 3DLUT files
shun-iwasawa ad7711
shun-iwasawa ad7711
  // The first line shoud start from "3DMESH" keyword (case sensitive)
shun-iwasawa ad7711
  line = locals::readDataLine(stream);
shun-iwasawa ad7711
  if (line != "3DMESH") {
shun-iwasawa ad7711
    file.close();
shun-iwasawa ad7711
    return execWarning(
shun-iwasawa ad7711
        QObject::tr("Failed to Load 3DLUT File.\nIt should start with "
shun-iwasawa ad7711
                    "\"3DMESH\" keyword."));
shun-iwasawa ad7711
  }
shun-iwasawa ad7711
shun-iwasawa ad7711
  // The second line is "Mesh [Input bit depth] [Output bit depth]"
shun-iwasawa ad7711
  // "Mesh" is a keyword (case sensitive) 
shun-iwasawa ad7711
  line             = locals::readDataLine(stream);
shun-iwasawa ad7711
  QStringList list = line.split(" ");
shun-iwasawa ad7711
  if (list.size() != 3 || list.at(0) != "Mesh") {
shun-iwasawa ad7711
    file.close();
shun-iwasawa ad7711
    return execWarning(
shun-iwasawa ad7711
        QObject::tr("Failed to Load 3DLUT File.\nThe second line should be "
shun-iwasawa ad7711
                    "\"Mesh [Input bit depth] [Output bit depth]\""));
shun-iwasawa ad7711
  }
shun-iwasawa ad7711
shun-iwasawa ad7711
  int inputBitDepth  = list.at(1).toInt();
shun-iwasawa ad7711
  int outputBitDepth = list.at(2).toInt();
shun-iwasawa ad7711
shun-iwasawa ad7711
  m_lut.meshSize = (int)pow(2.0, inputBitDepth) + 1;
shun-iwasawa ad7711
shun-iwasawa ad7711
  float maxValue = pow(2.0, outputBitDepth) - 1.0f;
shun-iwasawa ad7711
shun-iwasawa ad7711
  // The third line is corrections of values at each LUT grid
shun-iwasawa ad7711
  line = locals::readDataLine(stream);
shun-iwasawa ad7711
  list = line.split(" ", QString::SkipEmptyParts);
shun-iwasawa ad7711
  if (list.size() != m_lut.meshSize) {
shun-iwasawa ad7711
    file.close();
shun-iwasawa ad7711
    return execWarning(QObject::tr("Failed to Load 3DLUT File."));
shun-iwasawa ad7711
  }
shun-iwasawa ad7711
shun-iwasawa a8f111
  if (m_lut.data) delete[] m_lut.data;
shun-iwasawa ad7711
  m_lut.data = new float[m_lut.meshSize * m_lut.meshSize * m_lut.meshSize * 3];
shun-iwasawa ad7711
shun-iwasawa ad7711
  for (int k = 0; k < m_lut.meshSize; ++k)  // r
shun-iwasawa ad7711
  {
shun-iwasawa ad7711
    for (int j = 0; j < m_lut.meshSize; ++j)  // g
shun-iwasawa ad7711
    {
shun-iwasawa ad7711
      for (int i = 0; i < m_lut.meshSize; ++i)  // b
shun-iwasawa ad7711
      {
shun-iwasawa ad7711
        line = locals::readDataLine(stream);
shun-iwasawa ad7711
        list = line.split(" ", QString::SkipEmptyParts);
shun-iwasawa ad7711
        if (list.size() != 3) {
shun-iwasawa ad7711
          file.close();
shun-iwasawa ad7711
          delete[] m_lut.data;
shun-iwasawa ad7711
          return execWarning(QObject::tr("Failed to Load 3DLUT File."));
shun-iwasawa ad7711
        }
shun-iwasawa ad7711
        float* lut = m_lut.data + locals::lutCoords(k, j, i, m_lut.meshSize);
shun-iwasawa ad7711
        *lut       = (float)(list.at(0).toInt()) / maxValue;
shun-iwasawa ad7711
        lut++;
shun-iwasawa ad7711
        *lut = (float)(list.at(1).toInt()) / maxValue;
shun-iwasawa ad7711
        lut++;
shun-iwasawa ad7711
        *lut = (float)(list.at(2).toInt()) / maxValue;
shun-iwasawa ad7711
      }
shun-iwasawa ad7711
    }
shun-iwasawa ad7711
  }
shun-iwasawa ad7711
shun-iwasawa ad7711
  file.close();
shun-iwasawa ad7711
  return true;
shun-iwasawa ad7711
}
shun-iwasawa ad7711
//-----------------------------------------------------------------------------
shun-iwasawa ad7711
shun-iwasawa ad7711
// input : 0-1
shun-iwasawa 388550
void LutManager::convert(float& r, float& g, float& b) {
shun-iwasawa ad7711
  struct locals {
shun-iwasawa ad7711
    static inline float lerp(float val1, float val2, float ratio) {
shun-iwasawa ad7711
      return val1 * (1.0f - ratio) + val2 * ratio;
shun-iwasawa ad7711
    }
shun-iwasawa ad7711
    static inline int getCoord(int r, int g, int b, int meshSize) {
shun-iwasawa ad7711
      return b * meshSize * meshSize * 3 + g * meshSize * 3 + r * 3;
shun-iwasawa ad7711
    }
shun-iwasawa ad7711
  };
shun-iwasawa ad7711
shun-iwasawa ad7711
  if (!m_isValid) return;
shun-iwasawa ad7711
shun-iwasawa ad7711
  float ratio[3];   // RGB軸
shun-iwasawa ad7711
  int index[3][2];  // rgb インデックス
shun-iwasawa ad7711
  float rawVal[3] = {r, g, b};
shun-iwasawa ad7711
shun-iwasawa ad7711
  float vertex_color[2][2][2][3];  //補間用の1ボクセルの頂点色
shun-iwasawa ad7711
shun-iwasawa ad7711
  for (int c = 0; c < 3; c++) {
shun-iwasawa ad7711
    float val   = rawVal[c] * (float)(m_lut.meshSize - 1);
shun-iwasawa ad7711
    index[c][0] = (int)val;
shun-iwasawa ad7711
    // boundary condition: if rawVal == 1 the value will not be interporated
shun-iwasawa ad7711
    index[c][1] = (rawVal[c] >= 1.0f) ? index[c][0] : index[c][0] + 1;
shun-iwasawa ad7711
    ratio[c]    = val - (float)index[c][0];
shun-iwasawa ad7711
  }
shun-iwasawa ad7711
shun-iwasawa ad7711
  for (int rr = 0; rr < 2; rr++)
shun-iwasawa ad7711
    for (int gg = 0; gg < 2; gg++)
shun-iwasawa ad7711
      for (int bb = 0; bb < 2; bb++) {
shun-iwasawa ad7711
        float* val = &m_lut.data[locals::getCoord(
shun-iwasawa ad7711
            index[0][rr], index[1][gg], index[2][bb], m_lut.meshSize)];
shun-iwasawa e40777
        for (int chan = 0; chan < 3; chan++, val++)
shun-iwasawa ad7711
          vertex_color[rr][gg][bb][chan] = *val;
shun-iwasawa ad7711
      }
shun-iwasawa ad7711
  float result[3];
shun-iwasawa ad7711
shun-iwasawa ad7711
  for (int chan = 0; chan < 3; chan++) {
shun-iwasawa ad7711
    result[chan] = locals::lerp(
shun-iwasawa ad7711
        locals::lerp(locals::lerp(vertex_color[0][0][0][chan],
shun-iwasawa ad7711
                                  vertex_color[0][0][1][chan], ratio[2]),
shun-iwasawa ad7711
                     locals::lerp(vertex_color[0][1][0][chan],
shun-iwasawa ad7711
                                  vertex_color[0][1][1][chan], ratio[2]),
shun-iwasawa ad7711
                     ratio[1]),
shun-iwasawa ad7711
        locals::lerp(locals::lerp(vertex_color[1][0][0][chan],
shun-iwasawa ad7711
                                  vertex_color[1][0][1][chan], ratio[2]),
shun-iwasawa ad7711
                     locals::lerp(vertex_color[1][1][0][chan],
shun-iwasawa ad7711
                                  vertex_color[1][1][1][chan], ratio[2]),
shun-iwasawa ad7711
                     ratio[1]),
shun-iwasawa ad7711
        ratio[0]);
shun-iwasawa ad7711
  }
shun-iwasawa ad7711
shun-iwasawa ad7711
  r = result[0];
shun-iwasawa ad7711
  g = result[1];
shun-iwasawa ad7711
  b = result[2];
shun-iwasawa ad7711
}
shun-iwasawa ad7711
shun-iwasawa ad7711
//-----------------------------------------------------------------------------
shun-iwasawa ad7711
shun-iwasawa 388550
void LutManager::convert(QColor& col) {
shun-iwasawa ad7711
  if (!m_isValid) return;
shun-iwasawa ad7711
  float r = col.redF();
shun-iwasawa ad7711
  float g = col.greenF();
shun-iwasawa ad7711
  float b = col.blueF();
shun-iwasawa ad7711
  convert(r, g, b);
shun-iwasawa ad7711
  // 0.5 offset is necessary for converting to 255 grading
shun-iwasawa ad7711
  col = QColor((int)(r * 255.0 + 0.5), (int)(g * 255.0 + 0.5),
shun-iwasawa ad7711
               (int)(b * 255.0 + 0.5), col.alpha());
shun-iwasawa ad7711
}
shun-iwasawa ad7711
shun-iwasawa ad7711
//-----------------------------------------------------------------------------
shun-iwasawa ad7711
shun-iwasawa 388550
void LutManager::convert(TPixel32& col) {
shun-iwasawa ad7711
  if (!m_isValid) return;
shun-iwasawa ad7711
  float r = (float)col.r / 255.0;
shun-iwasawa ad7711
  float g = (float)col.g / 255.0;
shun-iwasawa ad7711
  float b = (float)col.b / 255.0;
shun-iwasawa ad7711
  convert(r, g, b);
shun-iwasawa ad7711
  col = TPixel32((int)(r * 255.0 + 0.5), (int)(g * 255.0 + 0.5),
shun-iwasawa ad7711
                 (int)(b * 255.0 + 0.5), col.m);
shun-iwasawa a8f111
}
shun-iwasawa a8f111
shun-iwasawa a8f111
//-----------------------------------------------------------------------------
shun-iwasawa a8f111
shun-iwasawa a8f111
void LutManager::registerCalibrator(LutCalibrator* calibrator) {
shun-iwasawa a8f111
  assert(!m_calibrators.contains(calibrator));
shun-iwasawa a8f111
  m_calibrators.insert(calibrator);
shun-iwasawa a8f111
}
shun-iwasawa a8f111
shun-iwasawa a8f111
//-----------------------------------------------------------------------------
shun-iwasawa a8f111
shun-iwasawa a8f111
void LutManager::removeCalibrator(LutCalibrator* calibrator) {
shun-iwasawa a8f111
  assert(m_calibrators.contains(calibrator));
shun-iwasawa a8f111
  m_calibrators.remove(calibrator);
shun-iwasawa a8f111
}
shun-iwasawa a8f111
shun-iwasawa a8f111
//-----------------------------------------------------------------------------
shun-iwasawa a8f111
shun-iwasawa a8f111
void LutManager::update() {
shun-iwasawa a8f111
  m_isValid           = false;
shun-iwasawa a8f111
  bool textureChanged = false;
shun-iwasawa a8f111
  if (Preferences::instance()->isColorCalibrationEnabled()) {
shun-iwasawa a8f111
    // obtain current monitor name
shun-iwasawa a8f111
    QString monitorName = getMonitorName();
shun-iwasawa a8f111
    // obtain 3dlut path associated to the monitor name
shun-iwasawa a8f111
    QString lutPath =
shun-iwasawa a8f111
        Preferences::instance()->getColorCalibrationLutPath(monitorName);
shun-iwasawa a8f111
    if (m_currentLutPath == lutPath)
shun-iwasawa a8f111
      m_isValid = true;
shun-iwasawa a8f111
    else if (loadLutFile(lutPath)) {
shun-iwasawa a8f111
      m_isValid        = true;
shun-iwasawa a8f111
      m_currentLutPath = lutPath;
shun-iwasawa a8f111
      textureChanged   = true;
shun-iwasawa a8f111
    }
shun-iwasawa a8f111
  }
shun-iwasawa a8f111
shun-iwasawa a8f111
  // update textures for all calibrators
shun-iwasawa a8f111
  for (auto calibrator : m_calibrators) calibrator->update(textureChanged);
shun-iwasawa a8f111
}