text: 
  
  // 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 : 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();
  }
  