Blob Blame Raw


#include "antialiaspopup.h"

// Tnz6 includes
#include "tapp.h"
#include "menubarcommandids.h"
#include "cellselection.h"
#include "filmstripselection.h"

// ToonzQt includes
#include "toonzqt/intfield.h"
#include "toonzqt/planeviewer.h"
#include "toonzqt/menubarcommand.h"
#include "toonzqt/tselectionhandle.h"
#include "toonzqt/icongenerator.h"

// TnzLib includes
#include "toonz/txshcell.h"
#include "toonz/txsheethandle.h"
#include "toonz/tframehandle.h"
#include "toonz/tcolumnhandle.h"
#include "toonz/txshlevelhandle.h"
#include "toonz/txshleveltypes.h"

// TnzCore includes
#include "tpixelgr.h"
#include "trasterimage.h"
#include "ttoonzimage.h"
#include "tpixelutils.h"
#include "tundo.h"
#include "trop.h"
#include "timagecache.h"

// Qt includes
#include <QSplitter>
#include <QScrollArea>
#include <QGridLayout>
#include <QPushButton>
#include <QLabel>
#include <QMainWindow>

//**************************************************************************
//    Local namespace stuff
//**************************************************************************

namespace
{

void onChange(const TRasterP &in, const TRasterP &out, int threshold, int softness)
{
	assert(in->getSize() == out->getSize());

	in->lock();
	out->lock();

	TRop::antialias(in, out, threshold, softness);

	in->unlock();
	out->unlock();
}

inline void onChange(const TRasterP &ras, int threshold, int softness)
{
	onChange(ras->clone(), ras, threshold, softness);
}

} // namespace

//**************************************************************************
//    AntialiasPopup Swatch
//**************************************************************************

class AntialiasPopup::Swatch : public PlaneViewer
{
	TImageP m_img;

public:
	Swatch(QWidget *parent = 0) : PlaneViewer(parent)
	{
		setBgColor(TPixel32::White, TPixel32::White);
	}

	TRasterP getRaster() const
	{
		return m_img->raster();
	}

	void setImage(const TImageP &img) { m_img = img; }

	void paintGL()
	{
		drawBackground();

		if (m_img) {
			glEnable(GL_BLEND);
			glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

			//Note GL_ONE instead of GL_SRC_ALPHA: it's needed since the input
			//image is supposedly premultiplied - and it works because the
			//viewer's background is opaque.
			//See tpixelutils.h's overPixT function for comparison.

			pushGLWorldCoordinates();
			draw(m_img);
			popGLCoordinates();

			glDisable(GL_BLEND);
		}
	}
};

//**************************************************************************
//    AntialiasPopup implementation
//**************************************************************************

AntialiasPopup::AntialiasPopup()
	: Dialog(TApp::instance()->getMainWindow(), true, false, "Antialias"), m_startRas(0)
{
	setWindowTitle(tr("Apply Antialias"));
	setLabelWidth(0);
	setModal(false);

	setTopMargin(0);
	setTopSpacing(0);

	beginVLayout();

	QSplitter *splitter = new QSplitter(Qt::Vertical);
	splitter->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding));
	addWidget(splitter);

	endVLayout();

	//------------------------- Top Layout --------------------------

	QScrollArea *scrollArea = new QScrollArea(splitter);
	scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
	scrollArea->setWidgetResizable(true);
	splitter->addWidget(scrollArea);
	splitter->setStretchFactor(0, 1);

	QFrame *topWidget = new QFrame(scrollArea);
	topWidget->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding));
	scrollArea->setWidget(topWidget);

	QGridLayout *topLayout = new QGridLayout(this);
	topWidget->setLayout(topLayout);

	//------------------------- Parameters --------------------------

	//Brightness
	QLabel *brightnessLabel = new QLabel(tr("Threshold:"));
	topLayout->addWidget(brightnessLabel, 0, 0, Qt::AlignRight | Qt::AlignVCenter);

	m_thresholdField = new DVGui::IntField(topWidget);
	m_thresholdField->setRange(0, 255);
	m_thresholdField->setValue(40);
	topLayout->addWidget(m_thresholdField, 0, 1);

	//Contrast
	QLabel *contrastLabel = new QLabel(tr("Softness:"));
	topLayout->addWidget(contrastLabel, 1, 0, Qt::AlignRight | Qt::AlignVCenter);

	m_softnessField = new DVGui::IntField(topWidget);
	m_softnessField->setRange(0, 100);
	m_softnessField->setValue(70);
	topLayout->addWidget(m_softnessField, 1, 1);

	topLayout->setRowStretch(2, 1);

	//--------------------------- Swatch ----------------------------

	m_viewer = new Swatch(splitter);
	m_viewer->setMinimumHeight(150);
	m_viewer->setFocusPolicy(Qt::WheelFocus);
	splitter->addWidget(m_viewer);

	//--------------------------- Button ----------------------------

	m_okBtn = new QPushButton(QString(tr("Apply")), this);
	connect(m_okBtn, SIGNAL(clicked()), this, SLOT(apply()));

	addButtonBarWidget(m_okBtn);

	//------------------------ Connections --------------------------

	TApp *app = TApp::instance();

	bool ret = true;

	ret = ret && connect(m_thresholdField, SIGNAL(valueChanged(bool)), this, SLOT(onValuesChanged(bool)));
	ret = ret && connect(m_softnessField, SIGNAL(valueChanged(bool)), this, SLOT(onValuesChanged(bool)));

	assert(ret);

	m_viewer->resize(0, 350);
	resize(600, 500);
}

//-----------------------------------------------------------------------------

void AntialiasPopup::setCurrentSampleRaster()
{
	TRasterP sampleRas;

	m_startRas = TRasterP();
	TSelection *selection = TApp::instance()->getCurrentSelection()->getSelection();
	TCellSelection *cellSelection = dynamic_cast<TCellSelection *>(selection);
	TFilmstripSelection *filmstripSelection = dynamic_cast<TFilmstripSelection *>(selection);
	TImageP image;
	if (cellSelection) {
		TApp *app = TApp::instance();
		TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
		TXshCell cell = xsh->getCell(app->getCurrentFrame()->getFrameIndex(), app->getCurrentColumn()->getColumnIndex());
		TImageP aux = cell.getImage(true);
		if (aux)
			image = aux->cloneImage();
	} else if (filmstripSelection) {
		TApp *app = TApp::instance();
		TXshSimpleLevel *simpleLevel = app->getCurrentLevel()->getSimpleLevel();
		if (simpleLevel) {
			TImageP imageAux = simpleLevel->getFrame(app->getCurrentFrame()->getFid(), true);
			if (imageAux)
				image = imageAux->cloneImage();
		}
	}

	if (!image || !(sampleRas = image->raster())) {
		m_viewer->setImage(TImageP());
		m_viewer->update();
		m_okBtn->setEnabled(false);
		return;
	}

	m_okBtn->setEnabled(true);
	m_startRas = sampleRas->clone();
	onChange(m_startRas, sampleRas, m_thresholdField->getValue(), m_softnessField->getValue());

	m_viewer->setImage(image);
	m_viewer->update();
}

//-----------------------------------------------------------------------------

void AntialiasPopup::showEvent(QShowEvent *se)
{
	TApp *app = TApp::instance();
	bool ret = true;
	ret = ret && connect(app->getCurrentFrame(), SIGNAL(frameTypeChanged()), this, SLOT(setCurrentSampleRaster()));
	ret = ret && connect(app->getCurrentFrame(), SIGNAL(frameSwitched()), this, SLOT(setCurrentSampleRaster()));
	ret = ret && connect(app->getCurrentColumn(), SIGNAL(columnIndexSwitched()), this, SLOT(setCurrentSampleRaster()));
	assert(ret);
	setCurrentSampleRaster();
}

//-----------------------------------------------------------------------------

void AntialiasPopup::hideEvent(QHideEvent *he)
{
	TApp *app = TApp::instance();
	disconnect(app->getCurrentFrame(), SIGNAL(frameTypeChanged()), this, SLOT(setCurrentSampleRaster()));
	disconnect(app->getCurrentFrame(), SIGNAL(frameSwitched()), this, SLOT(setCurrentSampleRaster()));
	disconnect(app->getCurrentColumn(), SIGNAL(columnIndexSwitched()), this, SLOT(setCurrentSampleRaster()));

	Dialog::hideEvent(he);

	m_viewer->setImage(TImageP());
	m_startRas = TRasterP();
}

//-----------------------------------------------------------------------------

class TRasterAntialiasUndo : public TUndo
{
	int m_r, m_c, m_threshold, m_softness;

	QString m_rasId;
	int m_rasSize;

public:
	TRasterAntialiasUndo(int threshold, int softness, int r, int c, TRasterP ras)
		: m_r(r), m_c(c), m_rasId(), m_threshold(threshold), m_softness(softness), m_rasSize(ras->getLx() * ras->getLy() * ras->getPixelSize())
	{
		m_rasId = QString("AntialiasUndo_") + QString::number(uintptr_t(this));
		TImageCache::instance()->add(m_rasId, TRasterImageP(ras));
	}

	~TRasterAntialiasUndo()
	{
		TImageCache::instance()->remove(m_rasId);
	}

	void undo() const
	{
		TXsheet *xsheet = TApp::instance()->getCurrentXsheet()->getXsheet();
		TXshCell cell = xsheet->getCell(m_r, m_c);
		TImageP image = cell.getImage(true);
		TRasterImageP rimg = (TRasterImageP)image;
		TToonzImageP timg = (TToonzImageP)image;
		if (!rimg && !timg)
			return;
		if (rimg)
			rimg->setRaster(((TRasterImageP)TImageCache::instance()->get(m_rasId, true))->getRaster()->clone());
		else
			timg->setCMapped(((TToonzImageP)TImageCache::instance()->get(m_rasId, true))->getRaster()->clone());

		TXshSimpleLevel *simpleLevel = cell.getSimpleLevel();
		assert(simpleLevel);
		simpleLevel->touchFrame(cell.getFrameId());
		simpleLevel->setDirtyFlag(false);
		IconGenerator::instance()->invalidate(simpleLevel, cell.getFrameId());

		if (m_isLastInBlock) {
			TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
		}
	}

	void redo() const
	{
		TXsheet *xsheet = TApp::instance()->getCurrentXsheet()->getXsheet();
		TXshCell cell = xsheet->getCell(m_r, m_c);
		TImageP image = (TRasterImageP)cell.getImage(true);
		if (!image)
			return;
		TRasterP ras = image->raster();
		if (!ras)
			return;

		onChange(ras, m_threshold, m_softness);
		TXshSimpleLevel *simpleLevel = cell.getSimpleLevel();
		assert(simpleLevel);
		simpleLevel->touchFrame(cell.getFrameId());
		simpleLevel->setDirtyFlag(false);
		IconGenerator::instance()->invalidate(simpleLevel, cell.getFrameId());
		if (m_isLastInBlock) {
			TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
		}
	}

	int getSize() const { return sizeof(*this) + m_rasSize; }
};

//-----------------------------------------------------------------------------

void AntialiasPopup::apply()
{
	TCellSelection *cellSelection = dynamic_cast<TCellSelection *>(TSelection::getCurrent());
	int threshold = m_thresholdField->getValue();
	int softness = m_softnessField->getValue();
	if (cellSelection) {
		std::set<TImage *> images;
		int r0, c0, r1, c1;
		cellSelection->getSelectedCells(r0, c0, r1, c1);
		TXsheet *xsheet = TApp::instance()->getCurrentXsheet()->getXsheet();
		bool oneImageChanged = false;
		int c, r;
		TUndoManager::manager()->beginBlock();
		for (c = c0; c <= c1; c++)
			for (r = r0; r <= r1; r++) {
				TXshCell cell = xsheet->getCell(r, c);
				TImageP image = cell.getImage(true);
				if (!image)
					continue;
				if (images.find(image.getPointer()) != images.end())
					continue;
				TRasterP ras = image->raster();
				if (!ras)
					continue;
				images.insert(image.getPointer());
				oneImageChanged = true;
				TUndoManager::manager()->add(new TRasterAntialiasUndo(threshold, softness, r, c, ras->clone()));
				onChange(ras, threshold, softness);
				TXshSimpleLevel *simpleLevel = cell.getSimpleLevel();
				assert(simpleLevel);
				simpleLevel->touchFrame(cell.getFrameId());
				simpleLevel->setDirtyFlag(true);
				IconGenerator::instance()->invalidate(simpleLevel, cell.getFrameId());
			}
		TUndoManager::manager()->endBlock();
		if (oneImageChanged) {
			close();
			return;
		}
	}
	TFilmstripSelection *filmstripSelection = dynamic_cast<TFilmstripSelection *>(TSelection::getCurrent());
	if (filmstripSelection) {
		TXshSimpleLevel *simpleLevel = TApp::instance()->getCurrentLevel()->getSimpleLevel();
		if (simpleLevel) {
			std::set<TFrameId> fids = filmstripSelection->getSelectedFids();
			std::set<TFrameId>::iterator it = fids.begin();
			bool oneImageChanged = false;
			for (it; it != fids.end(); it++) {
				TImageP image = simpleLevel->getFrame(*it, true);
				if (!image)
					continue;
				TRasterP ras = image->raster();
				if (!ras)
					continue;
				oneImageChanged = true;
				onChange(ras, threshold, softness);
				simpleLevel->touchFrame(*it);
				simpleLevel->setDirtyFlag(true);
				IconGenerator::instance()->invalidate(simpleLevel, *it);
			}
			if (oneImageChanged) {
				close();
				return;
			}
		}
	}

	DVGui::error(QObject::tr("The current selection is invalid."));
	return;
}

//-----------------------------------------------------------------------------

void AntialiasPopup::onValuesChanged(bool dragging)
{
	if (dragging || !m_startRas || !m_viewer->getRaster())
		return;

	onChange(m_startRas, m_viewer->getRaster(), m_thresholdField->getValue(), m_softnessField->getValue());
	m_viewer->update();
}

//-----------------------------------------------------------------------------

OpenPopupCommandHandler<AntialiasPopup> openAntialiasPopup(MI_Antialias);