Blob Blame Raw


// TnzCore includes
#include "tpalette.h"
#include "tstroke.h"
#include "tvectorimage.h"
#include "texception.h"
#include "tvectorrenderdata.h"
#include "tconvert.h"
#include "tofflinegl.h"
#include "tpixelutils.h"
#include "tflash.h"
#include "tcolorstyles.h"

//*****************************************************************************
//    Macros
//*****************************************************************************

#ifndef checkErrorsByGL
#define checkErrorsByGL                      \
	{                                        \
		GLenum err = glGetError();           \
		assert(err != GL_INVALID_ENUM);      \
		assert(err != GL_INVALID_VALUE);     \
		assert(err != GL_INVALID_OPERATION); \
		assert(err != GL_STACK_OVERFLOW);    \
		assert(err != GL_STACK_UNDERFLOW);   \
		assert(err != GL_OUT_OF_MEMORY);     \
		assert(err == GL_NO_ERROR);          \
	}
#endif

#undef checkErrorsByGL
#define checkErrorsByGL

//*****************************************************************************
//    TColorStyle  implementation
//*****************************************************************************

int TColorStyle::m_currentFrame = 0;

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

TColorStyle::TColorStyle()
	: m_name(L"color"), m_globalName(L""), m_originalName(L""), m_versionNumber(0), m_flags(0), m_enabled(true), m_icon(0), m_validIcon(false), m_isEditedFromOriginal(false)
{
}

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

TColorStyle::~TColorStyle()
{
}

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

TColorStyle::TColorStyle(const TColorStyle &other)
	: m_name(other.m_name), m_globalName(other.m_globalName), m_originalName(other.m_originalName), m_versionNumber(other.m_versionNumber), m_flags(other.m_flags), m_enabled(other.m_enabled), m_validIcon(false), m_isEditedFromOriginal(other.m_isEditedFromOriginal)
{
}

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

TColorStyle &TColorStyle::operator=(const TColorStyle &other)
{
	m_name = other.m_name;
	m_globalName = other.m_globalName;
	m_originalName = other.m_originalName;
	m_versionNumber = other.m_versionNumber;
	m_flags = other.m_flags;
	m_enabled = other.m_enabled;
	m_validIcon = false;
	m_isEditedFromOriginal = other.m_isEditedFromOriginal;

	return *this;
}

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

bool TColorStyle::operator==(const TColorStyle &cs) const
{
	if (getTagId() != cs.getTagId())
		return false;

	if (getMainColor() != cs.getMainColor())
		return false;

	int paramCount = getParamCount();
	if (paramCount != cs.getParamCount())
		return false;

	int colorParamCount = getColorParamCount();
	if (colorParamCount != cs.getColorParamCount())
		return false;

	if (m_name != cs.getName())
		return false;
	if (m_originalName != cs.getOriginalName())
		return false;
	if (m_globalName != cs.getGlobalName())
		return false;
	if (m_isEditedFromOriginal != cs.getIsEditedFlag())
		return false;

	for (int p = 0; p < colorParamCount; ++p)
		if (getColorParamValue(p) != cs.getColorParamValue(p))
			return false;

	for (int p = 0; p < paramCount; ++p) {
		switch (getParamType(p)) {
		case BOOL:
			if (getParamValue(bool_tag(), p) != cs.getParamValue(bool_tag(), p))
				return false;

			CASE INT : case ENUM : if (getParamValue(int_tag(), p) != cs.getParamValue(int_tag(), p)) return false;

			CASE DOUBLE : if (getParamValue(double_tag(), p) != cs.getParamValue(double_tag(), p)) return false;

			CASE FILEPATH : if (getParamValue(TFilePath_tag(), p) != cs.getParamValue(TFilePath_tag(), p)) return false;

		DEFAULT:
			assert(false);
		}
	}

	return true;
}

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

QString TColorStyle::getParamNames(int index) const
{
	assert(false);
	return QString("");
}

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

void TColorStyle::updateVersionNumber()
{
	++m_versionNumber;
}

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

const TRaster32P &TColorStyle::getIcon(const TDimension &d)
{
	checkErrorsByGL;
	if (!m_validIcon || !m_icon || m_icon->getSize() != d) {
		checkErrorsByGL;
		makeIcon(d);
		checkErrorsByGL;
		m_validIcon = true;
	}
	checkErrorsByGL;

	if (!m_icon) {
		checkErrorsByGL;
		TRaster32P icon(d);
		checkErrorsByGL;
		icon->fill(TPixel32::Black);
		checkErrorsByGL;
		int lx = icon->getLx();
		checkErrorsByGL;
		int ly = icon->getLy();
		checkErrorsByGL;
		for (int y = 0; y < ly; y++) {
			checkErrorsByGL;
			int x = ((lx - 1 - 10) * y / ly);
			checkErrorsByGL;
			icon->extractT(x, y, x + 5, y)->fill(TPixel32::Red);
			checkErrorsByGL;
		}
		checkErrorsByGL;
		m_icon = icon;
		checkErrorsByGL;
	}
	return m_icon;
}

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

void TColorStyle::makeIcon(const TDimension &d)
{
	checkErrorsByGL;
	TColorStyle *style = this->clone();
	checkErrorsByGL;

	TPaletteP tmpPalette = new TPalette();
	checkErrorsByGL;
	int id = tmpPalette->addStyle(style);
	checkErrorsByGL;

	int contextLx = pow(2.0, tceil(log((double)d.lx) / log(2.0)));
	int contextLy = pow(2.0, tceil(log((double)d.ly) / log(2.0)));
	TDimension dim(contextLx, contextLy);

	TOfflineGL *glContext = TOfflineGL::getStock(dim);

	checkErrorsByGL;
	glContext->clear(TPixel32::White);
	checkErrorsByGL;

	TVectorImageP img = new TVectorImage;
	checkErrorsByGL;
	img->setPalette(tmpPalette.getPointer());
	checkErrorsByGL;

	vector<TThickPoint> points(3);

	if (isRegionStyle() && !isStrokeStyle()) {
		points[0] = TThickPoint(-55, -50, 1);
		points[1] = TThickPoint(0, -60, 1);
		points[2] = TThickPoint(55, -50, 1);
		TStroke *stroke1 = new TStroke(points);

		img->addStroke(stroke1);

		points[0] = TThickPoint(50, -55, 1);
		points[1] = TThickPoint(60, 0, 1);
		points[2] = TThickPoint(50, 55, 1);
		TStroke *stroke2 = new TStroke(points);
		img->addStroke(stroke2);

		points[0] = TThickPoint(55, 50, 1);
		points[1] = TThickPoint(0, 60, 1);
		points[2] = TThickPoint(-55, 50, 1);
		TStroke *stroke3 = new TStroke(points);
		img->addStroke(stroke3);

		points[0] = TThickPoint(-50, 55, 1);
		points[1] = TThickPoint(-60, 0, 1);
		points[2] = TThickPoint(-50, -55, 1);
		TStroke *stroke4 = new TStroke(points);
		img->addStroke(stroke4);

		img->fill(TPointD(0, 0), id);
	} else if (isStrokeStyle() && !isRegionStyle()) {

		double rasX05 = d.lx * 0.5;
		double rasY05 = d.ly * 0.5;

		points[0] = TThickPoint(-rasX05, -rasY05, 7);
		points[1] = TThickPoint(0, -rasY05, 9);
		points[2] = TThickPoint(rasX05, rasY05, 12);
		TStroke *stroke1 = new TStroke(points);

		stroke1->setStyle(id);

		img->addStroke(stroke1);
		points.clear();
	} else if (!isRasterStyle()) {
		assert(isStrokeStyle() && isRegionStyle());

		points[0] = TThickPoint(-60, -30, 0.5);
		points[1] = TThickPoint(0, -30, 0.5);
		points[2] = TThickPoint(60, -30, 0.5);
		TStroke *stroke1 = new TStroke(points);
		stroke1->setStyle(id);
		img->addStroke(stroke1);

		points[0] = TThickPoint(60, -30, 0.5);
		points[1] = TThickPoint(60, 0, 0.5);
		points[2] = TThickPoint(60, 30, 0.5);
		TStroke *stroke2 = new TStroke(points);
		stroke2->setStyle(id);
		img->addStroke(stroke2);

		points[0] = TThickPoint(60, 30, 0.5);
		points[1] = TThickPoint(0, 30, 0.5);
		points[2] = TThickPoint(-60, 30, 0.5);
		TStroke *stroke3 = new TStroke(points);
		stroke3->setStyle(id);
		img->addStroke(stroke3);

		points[0] = TThickPoint(-60, 30, 0.5);
		points[1] = TThickPoint(-60, 0, 0.5);
		points[2] = TThickPoint(-60, -30, 0.5);
		TStroke *stroke4 = new TStroke(points);
		stroke4->setStyle(id);
		img->addStroke(stroke4);

		img->fill(TPointD(0, 0), id);
	}

	TRectD bbox = img->getBBox();
	checkErrorsByGL;

	bbox = bbox.enlarge(TDimensionD(-10, -10));
	checkErrorsByGL;

	double scx = 0.9 * d.lx / bbox.getLx();
	double scy = 0.9 * d.ly / bbox.getLy();
	double sc = tmin(scx, scy);
	double dx = (d.lx - bbox.getLx() * sc) * 0.5;
	double dy = (d.ly - bbox.getLy() * sc) * 0.5;
	TAffine aff = TScale(scx, scy) * TTranslation(-bbox.getP00() + TPointD(dx, dy));

	checkErrorsByGL;
	if (isRegionStyle() && !isStrokeStyle())
		aff = aff * TTranslation(-10, -10);

	checkErrorsByGL;
	const TVectorRenderData rd(aff, TRect(), tmpPalette.getPointer(), 0, true);
	checkErrorsByGL;
	glContext->draw(img, rd);
	checkErrorsByGL;

	TRect rect(d);
	if (!m_icon || m_icon->getSize() != d) {
		checkErrorsByGL;
		m_icon = glContext->getRaster()->extract(rect)->clone();
	} else {
		checkErrorsByGL;
		m_icon->copy(glContext->getRaster()->extract(rect));
	}
}

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

void TColorStyle::assignNames(const TColorStyle *src)
{
	m_name = src->getName();
	m_globalName = src->getGlobalName();
	m_originalName = src->getOriginalName();
	m_isEditedFromOriginal = src->getIsEditedFlag();
}

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

void TColorStyle::assignBlend(const TColorStyle &a, const TColorStyle &b, double t)
{
	// Blend colors
	{
		int col, colCount = getColorParamCount();
		assert(a.getColorParamCount() == colCount && b.getColorParamCount() == colCount);

		for (col = 0; col != colCount; ++col)
			setColorParamValue(col, blend(a.getColorParamValue(col), b.getColorParamValue(col), t));
	}

	// Blend parameters
	{
		int par, parCount = getParamCount();
		assert(a.getParamCount() == parCount && b.getParamCount() == parCount);

		for (par = 0; par != parCount; ++par) {
			switch (getParamType(par)) {
				CASE DOUBLE : setParamValue(par, (1 - t) * a.getParamValue(double_tag(), par) + t * b.getParamValue(double_tag(), par));

			DEFAULT:;
			}
		}
	}

	invalidateIcon();
}

//===================================================================
//
// color style global list
//
//===================================================================

namespace
{

class ColorStyleList
{ // singleton
	ColorStyleList() {}

	struct Item {
		TColorStyle *m_style;
		bool m_isObsolete;
		//    Item() : m_style(0), m_isObsolete(false) { assert(0); }
		Item(TColorStyle *style, bool obsolete = false)
			: m_style(style), m_isObsolete(obsolete) {}
	};

	typedef std::map<int, Item> Table;
	Table m_table;

public:
	static ColorStyleList *instance()
	{
		static ColorStyleList *_instance = 0;
		if (!_instance)
			_instance = new ColorStyleList();
		return _instance;
	}

	int getStyleCount()
	{
		return int(m_table.size());
	}

	void declare(TColorStyle *style)
	{
		int id = style->getTagId();
		if (m_table.find(id) != m_table.end()) {
			throw TException("Duplicate color style declaration. id = " + toString(id));
		}
		m_table.insert(std::make_pair(id, Item(style)));
		vector<int> ids;
		style->getObsoleteTagIds(ids);
		for (std::vector<int>::iterator it = ids.begin(); it != ids.end(); ++it) {
			if (m_table.find(*it) != m_table.end()) {
				throw TException("Duplicate color style declaration for obsolete style. id = " + toString(*it));
			}
			m_table.insert(std::make_pair(*it, Item(style->clone(), true)));
		}
	}

	TColorStyle *create(int id, bool &isObsolete)
	{
		Table::iterator it = m_table.find(id);
		if (it == m_table.end())
			throw TException("Unknown color style id; id = " + toString(id));

		isObsolete = it->second.m_isObsolete;

		return it->second.m_style->clone();
	}

	void getAllTags(vector<int> &tags)
	{
		tags.clear();
		tags.reserve(m_table.size());
		for (Table::iterator it = m_table.begin();
			 it != m_table.end(); ++it)
			if (!it->second.m_isObsolete)
				tags.push_back(it->first);
	}

	~ColorStyleList()
	{
		Table::iterator it = m_table.begin();
		for (; it != m_table.end(); ++it) {
			delete it->second.m_style;
		}
	}

private:
	// not implemented
	ColorStyleList(const ColorStyleList &);
	ColorStyleList &operator=(const ColorStyleList &);
};

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

} // namespace

//===================================================================

void TColorStyle::declare(TColorStyle *style)
{
	ColorStyleList::instance()->declare(style);
}

//===================================================================

double computeAverageThickness(const TStroke *s, double &minThickness, double &maxThickness)
{
	int count = s->getControlPointCount();

	minThickness = 1000;
	maxThickness = -1;
	double resThick = 0;

	for (int i = 0; i < s->getControlPointCount(); i++) {
		double thick = s->getControlPoint(i).thick;
		if (i >= 2 && i < s->getControlPointCount() - 2)
			resThick += thick;

		if (thick < minThickness)
			minThickness = thick;
		if (thick > maxThickness)
			maxThickness = thick;
	}

	if (count < 6)
		return s->getControlPoint(count / 2 + 1).thick;
	return resThick / (s->getControlPointCount() - 4);
}

void TColorStyle::drawStroke(TFlash &flash, const TStroke *s) const
{
	bool isCenterline = false;
	double minThickness, maxThickness = 0;
	wstring quality = flash.getLineQuality();
	double thickness = computeAverageThickness(s, minThickness, maxThickness);
	if (minThickness == maxThickness && minThickness == 0)
		return;
	if (quality == TFlash::ConstantLines)
		isCenterline = true;
	else if (quality == TFlash::MixedLines && (maxThickness == 0 || minThickness / maxThickness > 0.5))
		isCenterline = true;
	else if (quality == TFlash::VariableLines && maxThickness - minThickness < 0.16) // Quando si salva il pli, si approssima al thick.
																					 // L'errore di approx e' sempre 0.1568...
		isCenterline = true;
	//else	assert(false);

	flash.setFillColor(getAverageColor());
	//flash.setFillColor(TPixel::Red);

	TStroke *saux = const_cast<TStroke *>(s);
	if (isCenterline) {
		saux->setAverageThickness(thickness);
		flash.setThickness(s->getAverageThickness());
		flash.setLineColor(getAverageColor());
		flash.drawCenterline(s, false);
	} else {
		saux->setAverageThickness(0);
		if (!flash.drawOutline(saux)) {
			flash.setThickness(thickness);
			flash.setLineColor(getAverageColor());
			flash.drawCenterline(s, false);
		}
	}
}

//-----------------------------------------------------------------------------
// Format: _123 |global name
// _123 = flag; optional (*)
// |global = global name; optional
// name = color name; mandatory (??)

// note: If name starts with a digit or by '_', another '_'
// is added.
// (*): In such case, the flag is mandatory.
void TColorStyle::save(TOutputStreamInterface &os) const
{
	wstring name = getName();
	bool numberedName = !name.empty() && ('0' <= name[0] && name[0] <= '9' || name[0] == '_');

	if (m_flags > 0 || (name.length() == 1 && numberedName))
		os << ("_" + QString::number(m_flags)).toStdString();
	wstring gname = getGlobalName();
	wstring origName = getOriginalName();

	if (gname != L"") {
		os << toString(L"|" + gname);

		//save the original name from studio palette
		if (origName != L"") {
			//write two "@"s if the edited flag is ON
			os << toString(((m_isEditedFromOriginal) ? L"@@" : L"@") + origName);
		}
	}

	if (numberedName)
		name.insert(0, L"_");

	os << toString(name) << (int)getTagId();
	saveData(os);
}

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

TColorStyle *TColorStyle::load(TInputStreamInterface &is)
{
	string name;
	wstring gname;
	wstring origName;
	bool isEdited = false;

	is >> name;
	unsigned int flags = 0;
	if (name.length() == 2 && name[0] == '_' && '0' <= name[1] && name[1] <= '9') {
		flags = QString::fromStdString(name.substr(1)).toUInt();
		is >> name;
	}
	if (name.length() > 0 && name[0] == '|') {
		gname = toWideString(name.substr(1));
		is >> name;

		//If the style is copied from studio palette, original name is here
		if (name.length() > 0 && name[0] == '@') {
			//if there are two "@"s, then activate the edited flag
			if (name[1] == '@') {
				isEdited = true;
				origName = toWideString(name.substr(2));
			} else {
				origName = toWideString(name.substr(1));
			}
			is >> name;
		}
	}
	int id = 0;
	if (!name.empty() && '0' <= name[0] && name[0] <= '9') {
		id = toInt(name);
		name = "color";
	} else {
		if (!name.empty() && name[0] == '_')
			name.erase(name.begin());
		is >> id;
	}
	bool isObsolete = false;
	TColorStyle *style = ColorStyleList::instance()->create(id, isObsolete);
	assert(style);
	style->setFlags(flags);
	if (isObsolete)
		style->loadData(id, is);
	else
		style->loadData(is);
	style->setName(toWideString(name));
	style->setGlobalName(gname);
	style->setOriginalName(origName);
	style->setIsEditedFlag(isEdited);
	return style;
}

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

TColorStyle *TColorStyle::create(int tagId)
{
	bool isObsolete = false;
	return ColorStyleList::instance()->create(tagId, isObsolete);
}

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

void TColorStyle::getAllTags(vector<int> &tags)
{
	ColorStyleList::instance()->getAllTags(tags);
}