tahoma2d/toonz/sources/toonz/vectorizerswatch.cpp
Shinya Kitaoka d1f6c4e95b REFACTORING: Add final specifiers (#537)
* add final specifiers

* apply clang-format

* fix for macOS
2016-06-29 15:17:12 +09:00

527 lines
15 KiB
C++

// TnzCore includes
#include "tpalette.h"
#include "trasterimage.h"
#include "ttoonzimage.h"
#include "tvectorimage.h"
#include "drawutil.h"
// TnzLib includes
#include "toonz/toonzscene.h"
#include "toonz/sceneproperties.h"
#include "toonz/tscenehandle.h"
#include "toonz/txshsimplelevel.h"
#include "toonz/txshlevelhandle.h"
#include "toonz/txsheethandle.h"
#include "toonz/tframehandle.h"
#include "toonz/tcolumnhandle.h"
#include "toonz/stage.h"
// Tnz6 includes
#include "tapp.h"
// Qt includes
#include <QHBoxLayout>
#include <QList>
#include <QEvent>
#include "vectorizerswatch.h"
//*****************************************************************************
// Local namespace stuff
//*****************************************************************************
namespace {
inline void getCurrentXsheetPosition(int &row, int &col) {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
row = app->getCurrentFrame()->getFrame();
col = app->getCurrentColumn()->getColumnIndex();
}
//-----------------------------------------------------------------------------
inline TImageP getXsheetImage(int row, int col) {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
const TXshCell &cell = xsh->getCell(row, col);
TXshSimpleLevel *sl = cell.getSimpleLevel();
return sl
? sl->getFullsampledFrame(cell.getFrameId(),
ImageManager::dontPutInCache)
: TImageP();
}
//-----------------------------------------------------------------------------
std::auto_ptr<VectorizerConfiguration> getCurrentVectorizerConfiguration(
int row, int col) {
typedef std::auto_ptr<VectorizerConfiguration> result_type;
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
const ToonzScene *scene = app->getCurrentScene()->getScene();
assert(scene);
const VectorizerParameters *vParams =
scene->getProperties()->getVectorizerParameters();
assert(vParams);
const TXshCell &cell = xsh->getCell(row, col);
TXshSimpleLevel *sl = cell.getSimpleLevel();
if (!sl) return result_type();
int framesCount = sl->getFrameCount();
double vFrame =
(framesCount <= 1)
? 0.0
: tcrop((cell.getFrameId().getNumber() - 1) / double(framesCount - 1),
0.0, 1.0);
return result_type(vParams->getCurrentConfiguration(vFrame));
}
} // Local namespace
//*****************************************************************************
// VectorizationSwatchData declaration
//*****************************************************************************
namespace {
/*!
VectorizationSwatchData is the 'server' singleton data class used to
vectorize within swatch areas.
*/
struct VectorizationBuilder final : public TThread::Executor {
int m_row, m_col;
bool m_done, m_submitted;
TImageP m_image;
QList<VectorizerSwatchArea *> m_listeners;
public:
VectorizationBuilder()
: m_row(-1), m_col(-1), m_image(), m_done(false), m_submitted(false) {
setMaxActiveTasks(1);
}
static VectorizationBuilder *instance() {
static VectorizationBuilder theInstance;
return &theInstance;
}
void addListener(VectorizerSwatchArea *listener);
void removeListener(VectorizerSwatchArea *listener);
bool update(TImageP &img);
bool invalidate(TImageP &img);
void notifyDone(TImageP img);
};
} // Local namespace
//*****************************************************************************
// VectorizationBuilder implementation
//*****************************************************************************
void VectorizationBuilder::addListener(VectorizerSwatchArea *listener) {
m_listeners.push_back(listener);
}
//-----------------------------------------------------------------------------
void VectorizationBuilder::removeListener(VectorizerSwatchArea *listener) {
m_listeners.removeOne(listener);
if (m_listeners.empty()) {
m_image = TImageP();
m_row = m_col = -1;
m_done = false;
}
}
//-----------------------------------------------------------------------------
bool VectorizationBuilder::update(TImageP &img) {
int row, col;
getCurrentXsheetPosition(row, col);
bool computing = false;
if (row != m_row || col != m_col) {
m_row = row, m_col = col;
m_done = false;
addTask(new VectorizationSwatchTask(row, col));
computing = true;
}
img = m_image;
return computing;
}
//-----------------------------------------------------------------------------
bool VectorizationBuilder::invalidate(TImageP &img) {
m_row = m_col = -1;
m_done = false;
return update(img);
}
//-----------------------------------------------------------------------------
void VectorizationBuilder::notifyDone(TImageP img) {
m_image = img;
QList<VectorizerSwatchArea *>::iterator it;
for (it = m_listeners.begin(); it != m_listeners.end(); ++it) {
VectorizerSwatchArea::Swatch *swatch = (*it)->rightSwatch();
swatch->image() = img;
swatch->setDrawInProgress(false);
swatch->update();
}
}
//*****************************************************************************
// VectorizationSwatchTask implementation
//*****************************************************************************
VectorizationSwatchTask::VectorizationSwatchTask(int row, int col)
: m_row(row), m_col(col) {
// Establish connections to default slots; the started one must be blocking,
// so that run() awaits until it has been performed.
connect(this, SIGNAL(myStarted(TThread::RunnableP)),
SLOT(onStarted(TThread::RunnableP)), Qt::BlockingQueuedConnection);
connect(this, SIGNAL(finished(TThread::RunnableP)),
SLOT(onFinished(TThread::RunnableP)));
}
//-----------------------------------------------------------------------------
void VectorizationSwatchTask::onStarted(TThread::RunnableP task) {
VectorizationBuilder *builder = VectorizationBuilder::instance();
if (builder->m_done || builder->m_row != m_row || builder->m_col != m_col)
return;
// Retrieve the image to be vectorized
m_image = getXsheetImage(m_row, m_col);
// Build the vectorization configuration
m_config = getCurrentVectorizerConfiguration(m_row, m_col);
}
//-----------------------------------------------------------------------------
void VectorizationSwatchTask::run() {
emit myStarted(this);
if (!m_image || !m_config.get()) return;
// The task must be performed - retrieve and prepare configuration data
ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
VectorizerConfiguration *c = m_config.get();
// Build additional configuration data
TPaletteP palette;
TPointD center, dpi;
if (TToonzImageP ti = m_image) {
palette = ti->getPalette();
center = ti->getRaster()->getCenterD();
ti->getDpi(dpi.x, dpi.y);
} else if (TRasterImageP ri = m_image) {
palette = new TPalette;
center = ri->getRaster()->getCenterD();
ri->getDpi(dpi.x, dpi.y);
}
// In case the image has no dpi, use the standard dpi (same one the
// PlaneViewer will show
// the raster image with)
if (dpi.x == 0.0 || dpi.y == 0.0) dpi.x = dpi.y = Stage::inch;
// The vectorized image must feel the same visualization transform as m_image.
// We're transforming the image itself, rather than having it transformed at
// every paint event.
// NOTE: It would be best if the transform was the same found from the
// VectorizerPopup...
// Unfortunately, this would mean accessing the level's dpi value, thus having
// to rewrite quite
// some code around here...
c->m_affine =
TScale(Stage::inch / dpi.x, Stage::inch / dpi.y) * TTranslation(-center);
c->m_thickScale = Stage::inch / dpi.x;
// Vectorize the image
VectorizerCore vectorizer;
TVectorImageP vi = vectorizer.vectorize(m_image, *c, palette.getPointer());
vi->setPalette(palette.getPointer());
m_image = vi;
}
//-----------------------------------------------------------------------------
void VectorizationSwatchTask::onFinished(TThread::RunnableP task) {
if (m_image) {
VectorizationBuilder *builder = VectorizationBuilder::instance();
if (builder->m_row == m_row && builder->m_col == m_col) {
builder->m_done = true;
builder->notifyDone(m_image);
}
}
}
//*****************************************************************************
// VectorizerPopup::Swatch implementation
//*****************************************************************************
VectorizerSwatchArea::Swatch::Swatch(VectorizerSwatchArea *area)
: PlaneViewer(area)
, m_area(area)
, m_drawVectors(false)
, m_drawInProgress(false) {
setBgColor(TPixel32::White, TPixel32::White);
}
//-----------------------------------------------------------------------------
bool VectorizerSwatchArea::Swatch::event(QEvent *e) {
bool ret = PlaneViewer::event(e);
const TAffine &aff = (e->type() == QEvent::Resize)
? m_area->leftSwatch()->viewAff()
: viewAff();
m_area->updateView(aff);
return ret;
}
//-----------------------------------------------------------------------------
void VectorizerSwatchArea::Swatch::paintGL() {
if (isEnabled()) {
drawBackground();
if (m_img) {
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
pushGLWorldCoordinates();
draw(m_img);
if (m_drawVectors) drawVectors();
popGLCoordinates();
glDisable(GL_BLEND);
}
if (m_drawInProgress) drawInProgress();
} else {
glClearColor(0.6, 0.6, 0.6, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
}
}
//-----------------------------------------------------------------------------
void VectorizerSwatchArea::Swatch::drawVectors() {
TVectorImageP vi(m_img);
if (vi) {
glColor3d(1.0, 0.0, 0.0);
double pixelSize = sqrt(tglGetPixelSize2());
for (int i = 0; i < (int)vi->getStrokeCount(); ++i) {
TStroke *stroke = vi->getStroke(i);
drawStrokeCenterline(*stroke, pixelSize);
}
}
}
//-----------------------------------------------------------------------------
void VectorizerSwatchArea::Swatch::drawInProgress() {
glColor3d(1.0, 0.0, 0.0);
glLineWidth(3.0);
pushGLWinCoordinates();
glBegin(GL_LINE_STRIP);
glVertex2d(0.5, 0.5);
glVertex2d(width() - 0.5, 0.5);
glVertex2d(width() - 0.5, height() - 0.5);
glVertex2d(0.5, height() - 0.5);
glVertex2d(0.5, 0.5);
glEnd();
popGLCoordinates();
glLineWidth(1.0);
}
//*****************************************************************************
// VectorizerPopup::SwatchArea implementation
//*****************************************************************************
VectorizerSwatchArea::VectorizerSwatchArea(QWidget *parent) {
m_leftSwatch = new Swatch(this);
m_rightSwatch = new Swatch(this);
m_leftSwatch->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::MinimumExpanding));
m_rightSwatch->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::MinimumExpanding));
QHBoxLayout *lay = new QHBoxLayout;
setLayout(lay);
lay->addWidget(m_leftSwatch);
lay->addWidget(m_rightSwatch);
lay->setMargin(0);
setMinimumHeight(150);
// The followings help in re-establishing focus for wheel events
m_leftSwatch->setFocusPolicy(Qt::WheelFocus);
m_rightSwatch->setFocusPolicy(Qt::WheelFocus);
}
//-----------------------------------------------------------------------------
void VectorizerSwatchArea::updateView(const TAffine &aff) {
if (m_leftSwatch->viewAff() != aff)
m_leftSwatch->viewAff() = aff, m_leftSwatch->update();
if (m_rightSwatch->viewAff() != aff)
m_rightSwatch->viewAff() = aff, m_rightSwatch->update();
}
//-----------------------------------------------------------------------------
void VectorizerSwatchArea::resetView() {
m_leftSwatch->resetView();
m_rightSwatch->resetView();
}
//-----------------------------------------------------------------------------
void VectorizerSwatchArea::updateContents() {
if (!isEnabled() || !isVisible()) return;
// Retrieve current image
int row, col;
getCurrentXsheetPosition(row, col);
TImageP img = getXsheetImage(row, col);
// Update the image to be vectorized
if ((!img) || TVectorImageP(img)) {
m_leftSwatch->image() = TImageP();
m_rightSwatch->image() = TImageP();
} else {
m_leftSwatch->image() = img;
if (VectorizationBuilder::instance()->update(m_rightSwatch->image()))
m_rightSwatch->setDrawInProgress(true);
}
m_leftSwatch->update();
m_rightSwatch->update();
}
//-----------------------------------------------------------------------------
void VectorizerSwatchArea::invalidateContents() {
if (!isEnabled() || !isVisible()) return;
// Retrieve current image
int row, col;
getCurrentXsheetPosition(row, col);
TImageP img = getXsheetImage(row, col);
// Update the image to be vectorized
if ((!img) || TVectorImageP(img)) {
m_leftSwatch->image() = TImageP();
m_rightSwatch->image() = TImageP();
} else {
m_leftSwatch->image() = img;
if (VectorizationBuilder::instance()->invalidate(m_rightSwatch->image()))
m_rightSwatch->setDrawInProgress(true);
}
m_leftSwatch->update();
m_rightSwatch->update();
}
//-----------------------------------------------------------------------------
void VectorizerSwatchArea::enablePreview(bool enable) {
setEnabled(enable);
if (!isVisible()) return;
if (enable) {
connectUpdates();
updateContents();
} else
disconnectUpdates();
}
//-----------------------------------------------------------------------------
void VectorizerSwatchArea::enableDrawCenterlines(bool enable) {
m_rightSwatch->setDrawVectors(enable);
m_rightSwatch->update();
}
//-----------------------------------------------------------------------------
void VectorizerSwatchArea::connectUpdates() {
TFrameHandle *frameHandle = TApp::instance()->getCurrentFrame();
TXshLevelHandle *levelHandle = TApp::instance()->getCurrentLevel();
connect(frameHandle, SIGNAL(frameSwitched()), this, SLOT(updateContents()));
connect(levelHandle, SIGNAL(xshLevelSwitched(TXshLevel *)), this,
SLOT(updateContents()));
connect(levelHandle, SIGNAL(xshLevelChanged()), this,
SLOT(invalidateContents()));
VectorizationBuilder::instance()->addListener(this);
}
//-----------------------------------------------------------------------------
void VectorizerSwatchArea::disconnectUpdates() {
TFrameHandle *frameHandle = TApp::instance()->getCurrentFrame();
TXshLevelHandle *levelHandle = TApp::instance()->getCurrentLevel();
disconnect(frameHandle, 0, this, 0);
disconnect(levelHandle, 0, this, 0);
VectorizationBuilder::instance()->removeListener(this);
}
//-----------------------------------------------------------------------------
void VectorizerSwatchArea::showEvent(QShowEvent *se) {
if (isEnabled()) {
connectUpdates();
updateContents();
}
}
//-----------------------------------------------------------------------------
void VectorizerSwatchArea::hideEvent(QHideEvent *he) {
m_leftSwatch->image() = TImageP();
m_rightSwatch->image() = TImageP();
if (isEnabled()) disconnectUpdates();
}