Blob Blame Raw


// TnzCore includes
#include "tsystem.h"
#include "tthread.h"
#include "tstopwatch.h"

#include "tvectorgl.h"
#include "tvectorrenderdata.h"
#include "tvectorimage.h"
#include "trasterimage.h"
#include "tlevel_io.h"
#include "tstrokeprop.h"
#include "ttessellator.h"
#include "tcolorfunctions.h"
#include "tflash.h"
#include "tofflinegl.h"
#include "drawutil.h"
#include "trop.h"
#include "tstencilcontrol.h"
#include "tpalette.h"
#include "tpixelutils.h"
#include "tregionprop.h"
#include "tcurves.h"
#include "tcurveutil.h"
#include "tstrokeoutline.h"
#include "tregion.h"

#include "tsimplecolorstyles.h"

#ifndef _WIN32
#define CALLBACK
#endif

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

TFilePath TRasterImagePatternStrokeStyle::m_rootDir = TFilePath();
TFilePath TVectorImagePatternStrokeStyle::m_rootDir = TFilePath();

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

#ifndef NDEBUG
void checkGlError()
{
	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);
}

#define CHECK_GL_ERROR checkGlError();
#else
#define CHECK_GL_ERROR
#endif

// Use GL_LINE_STRIP to debug the centerline-to-outline algorithm
#define QUAD_PRIMITIVE GL_QUAD_STRIP

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

namespace
{

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

// se ras ha le dimensioni giuste (potenze di due) ritorna ras. Altrimenti
// crea il piu' piccolo raster con le dimensioni giuste che contenga ras e
// copia il contenuto (scalandolo)
// se il raster di partenza e' vuoto, non e' ras32 o e' piu' piccolo di 2x2
// ritorna un raster vuoto
TRaster32P makeTexture(const TRaster32P &ras)
{
	if (!ras || ras->getLx() < 2 || ras->getLy() < 2)
		return TRaster32P();
	TRaster32P ras32 = ras;
	if (!ras32)
		return TRaster32P();
	TDimension d(2, 2);
	while (d.lx < 256 && d.lx * 2 <= ras32->getLx())
		d.lx *= 2;
	while (d.ly < 256 && d.ly * 2 <= ras32->getLy())
		d.ly *= 2;
	if (d == ras32->getSize())
		return ras32;
	else {
		TRaster32P texture(d);
		TScale sc((double)d.lx / ras32->getLx(), (double)d.ly / ras32->getLy());
		TRop::resample(texture, ras32, sc);
		return texture;
	}
}

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

void drawAntialiasedOutline(const std::vector<TOutlinePoint> &_v, const TStroke *stroke)
{
	static const int stride = 2 * sizeof(TOutlinePoint);

	glEnableClientState(GL_VERTEX_ARRAY);

	glVertexPointer(2, GL_DOUBLE, stride, &_v[0]);
	glDrawArrays(GL_LINE_STRIP, 0, _v.size() / 2);

	glVertexPointer(2, GL_DOUBLE, stride, &_v[1]);
	glDrawArrays(GL_LINE_STRIP, 0, _v.size() / 2);

	glDisableClientState(GL_VERTEX_ARRAY);
}

} // namespace

//*************************************************************************************
//    DisplayListManager  definition
//*************************************************************************************

#ifdef _WIN32

namespace
{

class DisplayListManager // singleton
{
public:
	TThread::Mutex m_mutex;
	typedef HGLRC T_hGlContext;

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

	struct Key {
		string m_name;
		TFrameId m_fid;
		T_hGlContext m_glContext;
		Key(string name, TFrameId fid, T_hGlContext glContext)
			: m_name(name), m_fid(fid), m_glContext(glContext) {}
	};

	//----------------------------------------------
	struct Info {
		GLuint m_displayListId;
		TColorFunction::Parameters m_parameters;
		HDC m_hdc;
		Info()
			: m_displayListId(0), m_hdc(0), m_parameters(TColorFunction::Parameters()) {}

		Info(GLuint displayListId, HDC hdc, TColorFunction::Parameters parameters)
			: m_displayListId(displayListId), m_hdc(hdc), m_parameters(parameters) {}

		//se ad una stessa chiave corrispondono due displayInfo diversi (second questo metodo),
		//si aggiorna la displayList secondo l'ultima chiamata.
		//Serve per non duplicare troppe liste, per parametri che non cambiano spesso, come le cf.
		bool isDrawnAs(const Info &linfo)
		{
			return (
				this->m_parameters.m_mR == linfo.m_parameters.m_mR &&
				this->m_parameters.m_mG == linfo.m_parameters.m_mG &&
				this->m_parameters.m_mB == linfo.m_parameters.m_mB &&
				this->m_parameters.m_mM == linfo.m_parameters.m_mM &&
				this->m_parameters.m_cR == linfo.m_parameters.m_cR &&
				this->m_parameters.m_cG == linfo.m_parameters.m_cG &&
				this->m_parameters.m_cB == linfo.m_parameters.m_cB &&
				this->m_parameters.m_cM == linfo.m_parameters.m_cM);
		}
	};
	//----------------------------------------------

	struct KeyLess : public std::binary_function<Key, Key, bool> {
		bool operator()(const Key &d1, const Key &d2) const
		{
			return d1.m_glContext < d2.m_glContext ||
				   (d1.m_glContext == d2.m_glContext &&
					(d1.m_fid < d2.m_fid || d1.m_fid == d2.m_fid && d1.m_name < d2.m_name));
		}
	};

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

	typedef std::map<Key, Info, KeyLess> DisplayListMap;

private:
	DisplayListMap m_displayListMap;

	DisplayListManager() {}

	void recordList(GLuint listId, TVectorImage *image, const TVectorRenderData &rd)
	{
		QMutexLocker sl(&m_mutex);
		TRectD bbox = image->getBBox();
		double ry = bbox.getLy() * 0.5;
		if (ry < 1e-5)
			ry = 1e-5;
		TAffine identity;
		TRect rect;
		glPushMatrix();

		glLoadIdentity();
		// forzo pixelSize come se si disegnasse su una stroke con thickness=10
		//tglMultMatrix(TScale(10.0/ry));

		//forzo pixelSize a 20.
		//In questo modo, un'immagine piu' grande (nel pli
		//che definisce il custom style) viene piu' definita di una piu' piccola,
		//anche se appaiono della stessa dimensione (che dipende esclusivamente dalla thickness)
		//tglMultMatrix(TScale(0.50));

		TPalette *vPalette = image->getPalette();
		assert(vPalette);

		TVectorRenderData rd2(identity, rect, vPalette, rd.m_cf, true, true);
		rd2.m_isOfflineRender = rd.m_isOfflineRender;
		rd2.m_isImagePattern = true;

		CHECK_GL_ERROR
		glNewList(listId, GL_COMPILE);
		CHECK_GL_ERROR
		tglDraw(rd2, image);
		CHECK_GL_ERROR
		glEndList();
		CHECK_GL_ERROR
		glPopMatrix();
		CHECK_GL_ERROR
	}

public:
	static DisplayListManager *instance()
	{
		static DisplayListManager singleton;
		return &singleton;
	}
	//--------------------------------------------

	GLuint getDisplayListId(TVectorImage *image, string name, TFrameId fid, const TVectorRenderData &rd)
	{
		QMutexLocker sl(&m_mutex);
		TColorFunction::Parameters parameters;
		if (rd.m_cf)
			rd.m_cf->getParameters(parameters);
		CHECK_GL_ERROR

		HDC hDC = wglGetCurrentDC();
		T_hGlContext hContext = wglGetCurrentContext();
		Info listInfo(0, hDC, parameters);
		Key key(name, fid, hContext);
		DisplayListMap::iterator it = m_displayListMap.find(key);
		if (it != m_displayListMap.end()) {
			if (listInfo.isDrawnAs(it->second)) {
				//lascia tutto com'e' ed utilizza la displaliList
				listInfo.m_displayListId = it->second.m_displayListId;
			} else { //aggiorna la displayList con le nuove info (attualmente solo la cf)
				listInfo.m_displayListId = it->second.m_displayListId;
				recordList(listInfo.m_displayListId, image, rd);
				m_displayListMap[key] = listInfo;
			}
		} else //se non ho mai disegnato l'immagine creo la displayList
		{
			CHECK_GL_ERROR
			listInfo.m_displayListId = glGenLists(1);

			if (listInfo.m_displayListId != 0)
				recordList(listInfo.m_displayListId, image, rd);
			else
				std::cout << "list of " << name << fid << " is 0." << std::endl;

			CHECK_GL_ERROR
			m_displayListMap[key] = listInfo;
		}
		return listInfo.m_displayListId;
	}

	void clearLists()
	{
		//save current context and others
		HDC hCurrentDC = wglGetCurrentDC();
		T_hGlContext hCurrentContext = wglGetCurrentContext();
		//free all display lists
		DisplayListMap::iterator it = m_displayListMap.begin();
		for (; it != m_displayListMap.end(); ++it) {
			wglMakeCurrent(it->second.m_hdc, it->first.m_glContext);
			glDeleteLists(it->second.m_displayListId, 1);
		}
		m_displayListMap.clear();
		//restore current context

		wglMakeCurrent(hCurrentDC, hCurrentContext);
	}
};

} // namespace

#endif // _WIN32

//*************************************************************************************
//    TSimpleStrokeStyle  implementation
//*************************************************************************************

TStrokeProp *TSimpleStrokeStyle::makeStrokeProp(const TStroke *stroke)
{
	return new TSimpleStrokeProp(stroke, this);
}

//*************************************************************************************
//    TOutlineStyle  implementation
//*************************************************************************************

TOutlineStyle::TOutlineStyle()
	: m_regionOutlineModifier(0)
{
}

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

TOutlineStyle::TOutlineStyle(const TOutlineStyle &other)
	: TColorStyle(other), m_regionOutlineModifier(other.m_regionOutlineModifier ? other.m_regionOutlineModifier->clone() : 0)
{
}

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

TOutlineStyle::~TOutlineStyle()
{
	delete m_regionOutlineModifier;
}

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

void TOutlineStyle::setRegionOutlineModifier(RegionOutlineModifier *modifier)
{
	delete m_regionOutlineModifier;
	m_regionOutlineModifier = modifier;
}

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

TStrokeProp *TOutlineStyle::makeStrokeProp(const TStroke *stroke)
{
	return new OutlineStrokeProp(stroke, this);
}

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

TRegionProp *TOutlineStyle::makeRegionProp(const TRegion *region)
{
	return new OutlineRegionProp(region, this);
}

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

void TOutlineStyle::computeOutline(const TStroke *stroke,
								   TStrokeOutline &outline,
								   TOutlineUtil::OutlineParameter param) const
{
	TOutlineUtil::makeOutline(*stroke, outline, param);
}

//*************************************************************************************
//    TSolidColorStyle  implementation
//*************************************************************************************

TSolidColorStyle::TSolidColorStyle(const TPixel32 &color)
	: m_color(color), m_tessellator(new TglTessellator)
{
}

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

TSolidColorStyle::TSolidColorStyle(const TSolidColorStyle &other)
	: TOutlineStyle(other), m_color(other.m_color), m_tessellator(new TglTessellator)
{
}

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

TSolidColorStyle::~TSolidColorStyle()
{
	delete m_tessellator;
}

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

TColorStyle *TSolidColorStyle::clone() const
{
	return new TSolidColorStyle(*this);
}

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

void TSolidColorStyle::makeIcon(const TDimension &size)
{
	/*-- TSolidColorStyle, TColorCleanupStyle, TBlackCleanupStyle --*/
	if (getTagId() != 3 && getTagId() != 2001 && getTagId() != 2002) // e' un'istanza di una classe derivata da TSolidColorStyle
	{
		TColorStyle::makeIcon(size);
	} else {
		if (!m_icon || m_icon->getSize() != size) {
			TRaster32P ras(size);
			m_icon = ras;
		}
		TPixel32 col = m_color;
		if (col.m == 255)
			m_icon->fill(col);
		else {
			TRaster32P fg(size);
			fg->fill(premultiply(col));
			TRop::checkBoard(m_icon, TPixel32::Black, TPixel32::White, TDimensionD(6, 6), TPointD());
			TRop::over(m_icon, fg);
		}
	}
}

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

void TSolidColorStyle::drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const
{
	m_tessellator->tessellate(cf, antiAliasing, boundary, m_color);
}

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

void TSolidColorStyle::drawRegion(TFlash &flash, const TRegion *r) const
{
	flash.setFillColor(getMainColor());
	flash.setLineColor(TPixel::Transparent);
	flash.setThickness(0);
	flash.drawRegion(*r);
}

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

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

void TSolidColorStyle::drawStroke(const TColorFunction *cf, TStrokeOutline *outline, const TStroke *stroke) const
{
	struct locals {
		static inline void fillOutlinedStroke(const std::vector<TOutlinePoint> &v)
		{
			static const int stride = sizeof(TOutlinePoint);
			glEnableClientState(GL_VERTEX_ARRAY);
			glVertexPointer(2, GL_DOUBLE, stride, &v[0]);
			glDrawArrays(QUAD_PRIMITIVE, 0, v.size());
			glDisableClientState(GL_VERTEX_ARRAY);
		}
	}; // locals

	TPixel32 color = m_color;
	if (cf)
		color = (*cf)(color);

	if (color.m == 0)
		return;

	tglColor(color);

	const std::vector<TOutlinePoint> &v = outline->getArray();

	if (v.empty())
		return;

	if (true) // color.m != 254) // !dummyStyle)
	{
		if (color.m < 255) {
			TStencilControl *stencil = TStencilControl::instance();

			/*
    // invertendo l'ordine tra disegno interno e contorno, l'antialiasing
    // peggiora un po'. Piu che altro, il mangiucchiamento che faceva su alcune macchine, 
    // ma solo in drawOnScreen, ora lo fa anche quando si renderizza offline    
    stencil->beginMask(TStencilControl::DRAW_ON_SCREEN_ONLY_ONCE);
    // center line
    fillOutlinedStroke(v);    
    // outline with antialiasing
    drawAntialiasedOutline(v);                
    stencil->endMask();
    */

			stencil->beginMask(TStencilControl::DRAW_ON_SCREEN_ONLY_ONCE);
			locals::fillOutlinedStroke(v);
			stencil->endMask();
			stencil->enableMask(TStencilControl::SHOW_OUTSIDE);
			drawAntialiasedOutline(v, stroke);
			stencil->disableMask();

		} else {
			// outline with antialiasing
			drawAntialiasedOutline(v, stroke);

			// center line
			locals::fillOutlinedStroke(v);
		}

	} else {

		int i = 0;
		for (i = 0; i < stroke->getControlPointCount(); i++) {
			TThickPoint p = stroke->getControlPoint(i);
			if (i % 2) {
				tglColor(TPixel32(255, 0, 0));
				tglDrawCircle(p, p.thick);
			} else {
				tglColor(TPixel32(255, 200, 200));
				tglDrawDisk(p, p.thick);
				tglColor(TPixel32(0, 255, 0));
				tglDrawCircle(p, p.thick);
			}
			tglColor(TPixel32(127, 127, 127));
			tglDrawDisk(p, 0.3);
		}
		tglColor(TPixel32(127, 127, 127));
		glBegin(GL_LINE_STRIP);
		for (i = 0; i < stroke->getControlPointCount(); i++)
			tglVertex(stroke->getControlPoint(i));
		glEnd();

		tglColor(TPixel32(0, 0, 0));
		drawAntialiasedOutline(v, stroke);
	}
}

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

QString TSolidColorStyle::getDescription() const
{
	return "SolidColorStyle";
}

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

int TSolidColorStyle::getTagId() const
{
	return 3;
}

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

void TSolidColorStyle::setFill(TFlash &flash) const
{
	flash.setFillColor(getMainColor());
}

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

void TSolidColorStyle::loadData(TInputStreamInterface &is)
{
	TPixel32 color;
	is >> color;
	m_color = color;
}

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

void TSolidColorStyle::saveData(TOutputStreamInterface &os) const
{
	os << m_color;
}

//*************************************************************************************
//    TCenterLineStrokeStyle  implementation
//*************************************************************************************

TCenterLineStrokeStyle::TCenterLineStrokeStyle(
	const TPixel32 &color, USHORT stipple, double width)
	: m_color(color), m_stipple(stipple), m_width(width)
{
}

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

TColorStyle *TCenterLineStrokeStyle::clone() const
{
	return new TCenterLineStrokeStyle(*this);
}

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

void TCenterLineStrokeStyle::drawStroke(TFlash &flash, const TStroke *s) const
{
	if (m_width == 0)
		return;

	flash.setThickness(m_width);
	flash.setLineColor(getAverageColor());
	flash.drawCenterline(s, true);
	flash.setThickness(0);
}

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

void TCenterLineStrokeStyle::drawStroke(const TColorFunction *cf, const TStroke *stroke) const
{
	if (!stroke)
		return;

	// center line

	int i, n = stroke->getChunkCount();
	if (n == 0)
		return;
	TPixel32 color = m_color;
	if (cf)
		color = (*cf)(m_color);
	if (color.m == 0)
		return;
	tglColor(color);
	double pixelSize = sqrt(tglGetPixelSize2());

	if (m_stipple != 0) {
		glEnable(GL_LINE_STIPPLE);
		glLineStipple(1, m_stipple);
	}

	const TThickQuadratic *q = 0;
	q = stroke->getChunk(0);
	GLfloat rangeValue[2];
	glGetFloatv(GL_LINE_WIDTH_RANGE, &rangeValue[0]);

	float width = float(m_width);

	if (width < rangeValue[0])
		width = rangeValue[0];
	else if (width > rangeValue[1])
		width = rangeValue[1];

	width = 2 * width;

	if (width <= 1.0) {
		if (m_width != 0.0)
			width /= pixelSize;

		if (width == 0.F) // http://www.opengl.org/sdk/docs/man/xhtml/glLineWidth.xml
			width = 0.00001;

		glLineWidth(width);

		glBegin(GL_LINE_STRIP);
		for (i = 0; i < n; i++) {
			q = stroke->getChunk(i);
			double step = computeStep(*q, pixelSize);
			for (double t = 0; t < 1; t += step)
				tglVertex(q->getPoint(t));
		}
		assert(q);
		tglVertex(q->getP2());
		glEnd();
	} else {
		double s = 0;
		double length = stroke->getLength();
		TPointD pos1, pos4;
		vector<TPointD> pv;
		width = width / 2.;
		while (s <= length) {
			double step = 1.0;
			double w = stroke->getParameterAtLength(s);
			if (w <= 0) {
				s += 0.01;
				continue;
			} // per tamponare il baco della getParameterAtLength()
			TPointD u = stroke->getSpeed(w);
			if (norm2(u) == 0) {
				s += 0.1;
				continue;
			} // non dovrebbe succedere mai, ma per prudenza....
			TThickPoint pos = stroke->getThickPoint(w);
			u = normalize(u);
			TPointD v = rotate90(u) * (width);
			pos1 = pos + v;
			pos4 = pos - v;
			pv.push_back(pos1);
			pv.push_back(pos4);
			s += step;
		}
		double w = stroke->getParameterAtLength(length);
		if (w > 0) {

			TPointD u = stroke->getSpeed(w);
			if (norm2(u) != 0) {
				TThickPoint pos = stroke->getThickPoint(w);
				u = normalize(u);
				TPointD v = rotate90(u) * (width);
				pos1 = pos + v;
				pos4 = pos - v;
				pv.push_back(pos1);
				pv.push_back(pos4);
			}
		}

		if (pv.size() > 2) {
			glColor4ub(color.r, color.g, color.b, color.m);
			glBegin(QUAD_PRIMITIVE);
			std::vector<TPointD>::iterator it = pv.begin();
			for (; it != pv.end(); ++it) {
				tglVertex(*it);
			}
			glEnd();

			glBegin(GL_LINE_STRIP);

			std::vector<TPointD>::iterator it1 = pv.begin();
			for (; it1 != pv.end(); ++it1) {
				++it1;
				tglVertex(*it1);
			}
			glEnd();
			glBegin(GL_LINE_STRIP);
			std::vector<TPointD>::iterator it2 = pv.begin();
			if (it2 != pv.end())
				++it2;
			for (; it2 != pv.end() - 1; ++it2) {
				++it2;
				tglVertex(*it2);
			}
			glEnd();
		}
	}

	if (m_stipple != 0)
		glDisable(GL_LINE_STIPPLE);

	glLineWidth(1.0);
}

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

QString TCenterLineStrokeStyle::getDescription() const
{
	return QCoreApplication::translate("TCenterLineStrokeStyle", "Constant");
}

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

int TCenterLineStrokeStyle::getTagId() const
{
	return 2;
}

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

void TCenterLineStrokeStyle::loadData(TInputStreamInterface &is)
{
	is >> m_color >> m_stipple >> m_width;
}

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

void TCenterLineStrokeStyle::saveData(TOutputStreamInterface &os) const
{
	os << m_color << m_stipple << m_width;
}

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

int TCenterLineStrokeStyle::getParamCount() const
{
	return 1;
}

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

TColorStyle::ParamType TCenterLineStrokeStyle::getParamType(int index) const
{
	assert(0 <= index && index < getParamCount());
	return TColorStyle::DOUBLE;
}

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

QString TCenterLineStrokeStyle::getParamNames(int index) const
{
	return QCoreApplication::translate("TCenterLineStrokeStyle", "Thickness");
}

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

void TCenterLineStrokeStyle::getParamRange(int index, double &min, double &max) const
{
	assert(0 <= index && index < getParamCount());
	min = 0.0, max = 10.0;
}

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

double TCenterLineStrokeStyle::getParamValue(TColorStyle::double_tag, int index) const
{
	assert(0 <= index && index < getParamCount());
	return m_width;
}

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

void TCenterLineStrokeStyle::setParamValue(int index, double value)
{
	assert(0 <= index && index < getParamCount());
	m_width = value;
}

/*
TPixel32 TTextureStyle::getMainColor() const 
{return m_averageColor; }*/

//*************************************************************************************
//    TRasterImagePatternStrokeStyle  implementation
//*************************************************************************************

TRasterImagePatternStrokeStyle::TRasterImagePatternStrokeStyle()
	: m_level(), m_name(""), m_space(0), m_rotation(0)
{
}

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

TRasterImagePatternStrokeStyle::TRasterImagePatternStrokeStyle(const string &patternName)
	: m_level(), m_name(patternName), m_space(20), m_rotation(0)
{
	if (m_name != "")
		loadLevel(m_name);
}

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

TColorStyle *TRasterImagePatternStrokeStyle::clone() const
{
	return new TRasterImagePatternStrokeStyle(*this);
}

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

void TRasterImagePatternStrokeStyle::makeIcon(const TDimension &size)
{
	if (!m_level)
		loadLevel(m_name);
	m_icon = TRaster32P();
	TLevel::Iterator frameIt = m_level->begin();

	if (frameIt != m_level->end()) {
		TRasterImageP img = frameIt->second;
		TRaster32P src;
		if (img)
			src = img->getRaster();
		if (src && src->getLx() > 1 && src->getLy() > 1) {
			TRaster32P icon(size);
			icon->fill(TPixel32::White);
			double sx = (double)icon->getLx() / (double)src->getLx();
			double sy = (double)icon->getLy() / (double)src->getLy();
			double sc = 0.8 * tmin(sx, sy);
			TRop::resample(icon, src, TScale(sc).place(src->getCenterD(), icon->getCenterD()));
			TRop::addBackground(icon, TPixel32::White);
			m_icon = icon;
		}
	}

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

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

int TRasterImagePatternStrokeStyle::getParamCount() const
{
	return 2;
}

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

TColorStyle::ParamType TRasterImagePatternStrokeStyle::getParamType(int index) const
{
	assert(0 <= index && index < getParamCount());
	return TColorStyle::DOUBLE;
}

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

QString TRasterImagePatternStrokeStyle::getParamNames(int index) const
{
	assert(0 <= index && index < getParamCount());
	return (index == 0) ? QCoreApplication::translate("TRasterImagePatternStrokeStyle", "Distance") : QCoreApplication::translate("TRasterImagePatternStrokeStyle", "Rotation");
}

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

void TRasterImagePatternStrokeStyle::getParamRange(int index, double &min, double &max) const
{
	assert(0 <= index && index < getParamCount());

	if (index == 0) {
		min = -50;
		max = 50;
	} else {
		min = -180;
		max = 180;
	}
}

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

double TRasterImagePatternStrokeStyle::getParamValue(TColorStyle::double_tag, int index) const
{
	assert(0 <= index && index < getParamCount());
	return (index == 0) ? m_space : m_rotation;
}

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

void TRasterImagePatternStrokeStyle::setParamValue(int index, double value)
{
	assert(0 <= index && index < getParamCount());

	if (index == 0) {
		if (m_space != value) {
			updateVersionNumber(); //aggiorna il vettore di affini
		}
		m_space = value;
	} else {
		if (m_rotation != value) {
			updateVersionNumber(); //aggiorna il vettore di affini
		}
		m_rotation = value;
	}
}

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

//
// carico il pattern 'patternName' dalla directory dei custom styles
//
void TRasterImagePatternStrokeStyle::loadLevel(const string &patternName)
{
	struct locals {
		static TAffine getAffine(const TDimension &srcSize, const TDimension &dstSize)
		{
			double scx = 1 * dstSize.lx / (double)srcSize.lx;
			double scy = 1 * dstSize.ly / (double)srcSize.ly;
			double sc = tmin(scx, scy);
			double dx = (dstSize.lx - srcSize.lx * sc) * 0.5;
			double dy = (dstSize.ly - srcSize.ly * sc) * 0.5;
			return TScale(sc) * TTranslation(0.5 * TPointD(srcSize.lx, srcSize.ly) + TPointD(dx, dy));
		}
	}; // locals

	// button l'eventuale livello
	m_level = TLevelP();

	// aggiorno il nome
	m_name = patternName;

	// getRootDir() e' nulla se non si e' chiamata la setRoot(..)
	assert(!getRootDir().isEmpty());

	// leggo tutti i livelli contenuti
	TFilePathSet fps;
	TSystem::readDirectory(fps, getRootDir());

	// prendo il primo livello il cui nome sia patternName
	// (puo' essere un pli, ma anche un png, ecc.)
	TFilePath fp;
	TFilePathSet::iterator fpIt;
	for (fpIt = fps.begin(); fpIt != fps.end(); ++fpIt) {
		if (fpIt->getName() == patternName) {
			fp = *fpIt;
			break;
		}
	}

	// se non ho trovato nulla esco
	if (fp == TFilePath() || !TSystem::doesExistFileOrLevel(fp))
		return;

	// Leggo i frames del livello e ne ricavo delle textures
	// che memorizzo in m_level come TRasterImage
	TLevelReaderP lr(fp);
	TLevelP level = lr->loadInfo();
	TLevel::Iterator frameIt;
	TOfflineGL *glContext = 0;

	for (frameIt = level->begin(); frameIt != level->end(); ++frameIt) {
		TImageP img = lr->getFrameReader(frameIt->first)->load();
		if (TRasterImageP ri = img) {
			// se il frame e' raster...
			TRaster32P ras = ri->getRaster();
			if (!ras)
				continue;
			// aggiusta le dimensioni
			ras = makeTexture(ras);
			if (!ras)
				continue;
			m_level->setFrame(frameIt->first, new TRasterImage(ras));
		} else if (TVectorImageP vi = img) {
			// se il frame e' vettoriale
			// lo rasterizzo creando una texture 256x256 (N.B. le dimensioni
			// delle textures openGL devono essere potenze di due)
			TRaster32P ras(256, 256);
			// se serve creo il contesto OpenGL offline (l'idea e' di crearne
			// uno solo per tutto il livello
			if (!glContext)
				glContext = new TOfflineGL(ras->getSize());
			// camera di default 640x480. probabilmente non e' la scelta
			// migliore.
			TDimension cameraSize(768, 576);

			// definisco i renderdata
			const TVectorRenderData rd(
				locals::getAffine(cameraSize, ras->getSize()),
				TRect(),
				level->getPalette(),
				0, true, true);
			// rasterizzo
			glContext->draw(vi, rd);
			ras->copy(glContext->getRaster());
			m_level->setFrame(frameIt->first, new TRasterImage(ras));
		}
	}
	// cancello il contesto offline (se e' stato creato)
	delete glContext;
}

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

void TRasterImagePatternStrokeStyle::computeTransformations(vector<TAffine> &transformations, const TStroke *stroke) const
{
	const int frameCount = m_level->getFrameCount();
	if (frameCount == 0)
		return;
	transformations.clear();
	const double length = stroke->getLength();

	std::vector<TDimensionD> images;
	assert(m_level->begin() != m_level->end());
	TLevel::Iterator lit;
	for (lit = m_level->begin(); lit != m_level->end(); ++lit) {
		TRasterImageP ri = lit->second;
		if (!ri)
			continue;
		TDimension d = ri->getRaster()->getSize();
		images.push_back(TDimensionD(d.lx, d.ly));
	}
	assert(!images.empty());
	if (images.empty())
		return;

	double s = 0;
	int index = 0;
	int m = images.size();
	while (s < length) {
		double t = stroke->getParameterAtLength(s);
		TThickPoint p = stroke->getThickPoint(t);
		TPointD v = stroke->getSpeed(t);
		double ang = rad2degree(atan(v)) + m_rotation;

		int ly = tmax(1.0, images[index].ly);
		double sc = p.thick / ly;
		transformations.push_back(TTranslation(p) * TRotation(ang) * TScale(sc));
		double ds = tmax(2.0, sc * images[index].lx * 2 + m_space);
		s += ds;
	}
}

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

void TRasterImagePatternStrokeStyle::drawStroke(
	const TVectorRenderData &rd,
	const vector<TAffine> &transformations,
	const TStroke *stroke) const
{
	TStopWatch sw;
	sw.start();
	CHECK_GL_ERROR

	const int frameCount = m_level->getFrameCount();
	if (frameCount == 0)
		return;

	// lo stroke viene disegnato ripetendo size volte le frameCount immagini
	// contenute in level, posizionando ognuna secondo transformations[i]
	UINT size = transformations.size();

	glEnable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);

	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	GLuint texId;
	glGenTextures(1, &texId);

	glBindTexture(GL_TEXTURE_2D, texId);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	// l'utilizzo di modulate, con un colore di 1,1,1,1 permette di
	// tenere in conto il canale alfa delle texture
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);

	// visto che cambiare texture costa tempo il ciclo esterno e' sulle textures
	// piuttosto che sulle trasformazioni
	TLevel::Iterator frameIt = m_level->begin();
	for (int i = 0; i < (int)size && frameIt != m_level->end(); ++i, ++frameIt) {
		TRasterImageP ri = frameIt->second;
		TRasterP ras;
		if (ri)
			ras = ri->getRaster();
		if (!ras)
			continue;
		TextureInfoForGL texInfo;
		TRasterP texImage = prepareTexture(ras, texInfo);

		glTexImage2D(GL_TEXTURE_2D,
					 0,						 // one level only
					 texInfo.internalformat, // pixel channels count
					 texInfo.width,			 // width
					 texInfo.height,		 // height
					 0,						 // border size
					 texInfo.type,			 // pixel format           // crappy names
					 texInfo.format,		 // pixel data type        // oh, SO much
					 texImage->getRawData());

		for (int j = i; j < (int)size; j += frameCount) {
			TAffine aff = rd.m_aff * transformations[j];
			glPushMatrix();
			tglMultMatrix(aff);

			double rx = ras->getLx();
			double ry = ras->getLy();

			glColor4d(1, 1, 1, 1);

			glBegin(QUAD_PRIMITIVE);
			glTexCoord2d(0, 0);
			glVertex2d(-rx, -ry);
			glTexCoord2d(1, 0);
			glVertex2d(rx, -ry);
			glTexCoord2d(0, 1);
			glVertex2d(-rx, ry);
			glTexCoord2d(1, 1);
			glVertex2d(rx, ry);
			glEnd();

			glPopMatrix();
		}
	}

	glDeleteTextures(1, &texId);

	glDisable(GL_TEXTURE_2D);
	glDisable(GL_BLEND);
}

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

void TRasterImagePatternStrokeStyle::drawStroke(TFlash &flash, const TStroke *stroke) const
{
	flash.drawHangedObjects();
	if (m_level->getFrameCount() == 0) {
		//if( rd.m_clippingRect!=TRect() && ! convert(rd.m_aff*stroke->getBBox()).overlaps( rd.m_clippingRect ) ) return;
		TCenterLineStrokeStyle *appStyle = new TCenterLineStrokeStyle(TPixel32(255, 0, 0, 255), 0x0, 2.0);
		//flash.pushMatrix();
		//flash.multMatrix(rd.m_aff);
		appStyle->drawStroke(flash, stroke);
		//glPopMatrix();
		return;
	}

	vector<TAffine> transformations;
	computeTransformations(transformations, stroke);
	assert(m_level->begin() != m_level->end());
	TLevel::Iterator lit = m_level->begin();

	for (UINT i = 0; i < transformations.size(); i++) {
		TRasterImageP img = m_level->frame(lit->first);
		if (++lit == m_level->end())
			lit = m_level->begin();
		assert(img);
		flash.pushMatrix();
		flash.multMatrix(transformations[i] * TScale(2.0));
		flash.buildImage(img.getPointer(), false);
		flash.popMatrix();
	}
}

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

void TRasterImagePatternStrokeStyle::loadData(TInputStreamInterface &is)
{
	m_level = TLevelP();
	m_name = "";
	string name;
	is >> name >> m_space >> m_rotation;
	if (name != "") {
		try {
			loadLevel(name);
		} catch (...) {
		}
	}
}

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

void TRasterImagePatternStrokeStyle::loadData(int ids, TInputStreamInterface &is)
{
	if (ids != 100)
		throw TException("image pattern stroke style: unknown obsolete format");

	m_level = TLevelP();
	m_name = "";
	string name;
	is >> name;
	if (name != "") {
		try {
			loadLevel(name);
		} catch (...) {
		}
	}
}

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

void TRasterImagePatternStrokeStyle::saveData(TOutputStreamInterface &os) const
{
	os << m_name << m_space << m_rotation;
}

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

TFilePath TRasterImagePatternStrokeStyle::getRootDir()
{
	return m_rootDir;
}

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

TStrokeProp *TRasterImagePatternStrokeStyle::makeStrokeProp(const TStroke *stroke)
{
	return new TRasterImagePatternStrokeProp(stroke, this);
}

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

void TRasterImagePatternStrokeStyle::getObsoleteTagIds(vector<int> &ids) const
{
	ids.push_back(100);
}

//*************************************************************************************
//    TVectorImagePatternStrokeStyle  implementation
//*************************************************************************************

TVectorImagePatternStrokeStyle::TVectorImagePatternStrokeStyle()
	: m_level(), m_name(""), m_space(0), m_rotation(0)
{
}

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

TVectorImagePatternStrokeStyle::TVectorImagePatternStrokeStyle(const string &patternName)
	: m_level(), m_name(patternName), m_space(20), m_rotation(0)
{
	loadLevel(patternName);
}

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

TColorStyle *TVectorImagePatternStrokeStyle::clone() const
{
	return new TVectorImagePatternStrokeStyle(*this);
}

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

void TVectorImagePatternStrokeStyle::makeIcon(const TDimension &size)
{
	TLevel::Iterator frameIt = m_level->begin();
	if (frameIt == m_level->end()) {
		TRaster32P icon(size);
		icon->fill(TPixel32::Black);
		int lx = icon->getLx();
		int ly = icon->getLy();
		for (int y = 0; y < ly; y++) {
			int x = ((lx - 1 - 10) * y / ly);
			icon->extractT(x, y, x + 5, y)->fill(TPixel32::Red);
		}
		m_icon = icon;
		return;
	}
	TFrameId fid = frameIt->first;
	TVectorImageP img = m_level->frame(fid);
	if (!img) {
		TRaster32P icon(size);
		icon->fill(TPixel32::Black);
		int lx = icon->getLx();
		int ly = icon->getLy();
		for (int y = 0; y < ly; y++) {
			int x = ((lx - 1 - 10) * y / ly);
			icon->extractT(x, y, x + 5, y)->fill(TPixel32::Red);
		}
		m_icon = icon;
		return;
	}
	//img->setPalette(m_level->getPalette());
	TPalette *vPalette = m_level->getPalette();
	assert(vPalette);
	img->setPalette(vPalette);
	TOfflineGL *glContext = TOfflineGL::getStock(size);
	glContext->clear(TPixel32::White);
	TRectD bbox = img->getBBox();
	double scx = 0.8 * size.lx / bbox.getLx();
	double scy = 0.8 * size.ly / bbox.getLy();
	double sc = tmin(scx, scy);
	double dx = (size.lx - bbox.getLx() * sc) * 0.5;
	double dy = (size.ly - bbox.getLy() * sc) * 0.5;
	TAffine aff = TScale(sc) * TTranslation(-bbox.getP00() + TPointD(dx, dy));
	TVectorRenderData rd(aff, size, vPalette, 0, true);
	glContext->draw(img, rd);
	if (!m_icon || m_icon->getSize() != size)
		m_icon = glContext->getRaster()->clone();
	else
		m_icon->copy(glContext->getRaster());
}

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

int TVectorImagePatternStrokeStyle::getParamCount() const
{
	return 2;
}

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

TColorStyle::ParamType TVectorImagePatternStrokeStyle::getParamType(int index) const
{
	assert(0 <= index && index < getParamCount());
	return TColorStyle::DOUBLE;
}

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

QString TVectorImagePatternStrokeStyle::getParamNames(int index) const
{
	assert(0 <= index && index < getParamCount());
	return (index == 0) ? QCoreApplication::translate("TVectorImagePatternStrokeStyle", "Distance") : QCoreApplication::translate("TVectorImagePatternStrokeStyle", "Rotation");
}

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

void TVectorImagePatternStrokeStyle::getParamRange(int index, double &min, double &max) const
{
	assert(0 <= index && index < getParamCount());

	if (index == 0) {
		min = -50;
		max = 50;
	} else {
		min = -180;
		max = 180;
	}
}

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

double TVectorImagePatternStrokeStyle::getParamValue(TColorStyle::double_tag, int index) const
{
	assert(0 <= index && index < getParamCount());
	return index == 0 ? m_space : m_rotation;
}

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

void TVectorImagePatternStrokeStyle::setParamValue(int index, double value)
{
	assert(0 <= index && index < getParamCount());

	if (index == 0) {
		if (m_space != value) {
			updateVersionNumber(); //aggiorna il vettore di affini
		}
		m_space = value;
	} else {
		if (m_rotation != value) {
			updateVersionNumber(); //aggiorna il vettore di affini
		}
		m_rotation = value;
	}
}

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

void TVectorImagePatternStrokeStyle::loadLevel(const string &patternName)
{
	m_level = TLevelP();
	m_name = patternName;
	assert(!getRootDir().isEmpty()); //se e' vuota, non si e' chiamata la setRoot(..)
	TFilePath fp = getRootDir() + (patternName + ".pli");
	TLevelReaderP lr(fp);
	m_level = lr->loadInfo();
	TLevel::Iterator frameIt;
	map<TPixel32, int> colors;
	for (frameIt = m_level->begin(); frameIt != m_level->end(); ++frameIt) {
		TVectorImageP img = lr->getFrameReader(frameIt->first)->load();
		if (img)
			m_level->setFrame(frameIt->first, img);
	}
}

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

void TVectorImagePatternStrokeStyle::computeTransformations(vector<TAffine> &transformations, const TStroke *stroke) const
{
	const int frameCount = m_level->getFrameCount();
	if (frameCount == 0)
		return;
	transformations.clear();
	const double length = stroke->getLength();
	assert(m_level->begin() != m_level->end());
	TLevel::Iterator lit = m_level->begin();
	double s = 0;

	while (s < length) {
		TFrameId fid = lit->first;
		TVectorImageP img = m_level->frame(fid);
		if (++lit == m_level->end())
			lit = m_level->begin();
		assert(img);
		if (img->getType() != TImage::VECTOR)
			return;
		double t = stroke->getParameterAtLength(s);
		TThickPoint p = stroke->getThickPoint(t);
		TPointD v = stroke->getSpeed(t);
		double ang = rad2degree(atan(v)) + m_rotation;
		TRectD bbox = img->getBBox();
		TPointD center = 0.5 * (bbox.getP00() + bbox.getP11());
		double ry = bbox.getLy() * 0.5;
		if (ry * ry < 1e-5)
			ry = p.thick;
		double sc = p.thick / ry;
		if (sc < 0.0001)
			sc = 0.0001;
		TAffine aff =
			TTranslation(p) * TRotation(ang) * TScale(sc) * TTranslation(-center);
		transformations.push_back(aff);
		double ds = tmax(2.0, sc * bbox.getLx() + m_space);
		s += ds;
	}
}

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

void TVectorImagePatternStrokeStyle::clearGlDisplayLists()
{
#ifdef _WIN32
	DisplayListManager *pmgr = DisplayListManager::instance();
	assert(pmgr);
	pmgr->clearLists();
#endif
}

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

void TVectorImagePatternStrokeStyle::drawStroke(const TVectorRenderData &rd, const vector<TAffine> &transformations, const TStroke *stroke) const
{
	const int frameCount = m_level->getFrameCount();
	if (frameCount == 0)
		return;
	//------------------------------------------
	// if the average thickness of the stroke, with the current pixelSize,
	// is less than 2, do not drow anything
	UINT cpCount = stroke->getControlPointCount();
	UINT sampleStep = (cpCount < 10) ? 1 : (UINT)((double)cpCount / 10.0);
	double thickSum = 0;
	UINT count = 0;
	for (UINT cp = 0; cp < cpCount; cp += sampleStep) {
		thickSum += stroke->getControlPoint(cp).thick;
		count++;
	}
	double averageThick = thickSum / (double)count;
	glPushMatrix();
	tglMultMatrix(rd.m_aff);
	double pixelSize2 = tglGetPixelSize2();
	glPopMatrix();
	if (averageThick * averageThick < 4 * pixelSize2) {
		CHECK_GL_ERROR

		glPushMatrix();

		CHECK_GL_ERROR

		tglMultMatrix(rd.m_aff);

		TLevel::Iterator lit = m_level->begin();

		TFrameId fid = lit->first;
		TVectorImageP img = m_level->frame(fid);

		std::set<int> styles;
		img->getUsedStyles(styles);

		std::set<int>::iterator si = styles.begin();

		// Calcolo media dei colori utilizzati

		int numStyles = 0, styleID;
		int averageRed = 0, averageBlue = 0, averageGreen = 0;
		TPixel32 color, averageColor;
		TColorStyle *colorStyle;

		for (; si != styles.end(); si++) {
			styleID = (int)*si;

			if (styleID != 0) {
				colorStyle = m_level->getPalette()->getStyle(styleID);
				color = colorStyle->getAverageColor();
				if (color.m != 0) {
					numStyles++;
					averageRed += color.r;
					averageGreen += color.g;
					averageBlue += color.b;
				}
			}
		}

		if (numStyles != 0) {
			averageColor.r = averageRed / numStyles;
			averageColor.g = averageGreen / numStyles;
			averageColor.b = averageBlue / numStyles;
		} else
			averageColor = TPixel32::Black;

		tglColor(averageColor);

		glLineStipple(2, 0xAAAA);
		glEnable(GL_LINE_STIPPLE);

		UINT n = stroke->getLength() * 0.05; // TODO: funzione di stroke length e pixelsize
		glBegin(GL_LINE_STRIP);
		for (UINT i = 0; i < n; i++) {
			if (n != 1) {
				TThickPoint p = stroke->getPoint(i / (double)(n - 1));
				glVertex2d(p.x, p.y);
			}
		}
		glEnd();

		glDisable(GL_LINE_STIPPLE);

		CHECK_GL_ERROR

		glPopMatrix();

	} else {
		//--------------------------------------------
		assert(m_level->begin() != m_level->end());
		TLevel::Iterator lit = m_level->begin();
		UINT i, size = transformations.size();

		for (i = 0; i < size; i++) {
			TFrameId fid = lit->first;
			TVectorImageP img = m_level->frame(fid);
			if (++lit == m_level->end())
				lit = m_level->begin();
			assert(img);
			if (!img)
				continue;
			if (img->getType() != TImage::VECTOR)
				return;
			TVectorImage *imgPointer = img.getPointer();
			TAffine totalTransformation = rd.m_aff * transformations[i];
			if (rd.m_clippingRect != TRect() && !rd.m_is3dView &&
				!(convert(totalTransformation * imgPointer->getBBox()).overlaps(rd.m_clippingRect)))
				continue;
			// drawing
			CHECK_GL_ERROR

			glPushMatrix();

			CHECK_GL_ERROR

			tglMultMatrix(totalTransformation);

			CHECK_GL_ERROR
#ifdef _WIN32

			GLuint listId = DisplayListManager::instance()->getDisplayListId(imgPointer, m_name, fid, rd);
			if (listId != 0) {
				CHECK_GL_ERROR
				glCallList(listId);
				CHECK_GL_ERROR
			} else
#endif

			{
				TAffine identity;
				TRect rect;
				TPalette *vPalette = imgPointer->getPalette();
				assert(vPalette);
				TVectorRenderData rd2(identity, rect, vPalette, rd.m_cf, true, true);
				CHECK_GL_ERROR

				tglDraw(rd2, imgPointer);

				CHECK_GL_ERROR
			}
			glPopMatrix();
		}
	}
}

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

void TVectorImagePatternStrokeStyle::drawStroke(TFlash &flash, const TStroke *stroke) const
{
	flash.drawHangedObjects();

	const int frameCount = m_level->getFrameCount();
	if (frameCount == 0) {
		//if( rd.m_clippingRect!=TRect() && ! convert(rd.m_aff*stroke->getBBox()).overlaps( rd.m_clippingRect ) ) return;
		TCenterLineStrokeStyle *appStyle = new TCenterLineStrokeStyle(TPixel32(255, 0, 0, 255), 0x0, 2.0);
		//flash.pushMatrix();
		//flash.multMatrix(rd.m_aff);
		appStyle->drawStroke(flash, stroke);
		//glPopMatrix();
		return;
	}
	UINT cpCount = stroke->getControlPointCount();
	UINT sampleStep = (cpCount < 10) ? 1 : (UINT)((double)cpCount / 10.0);

	double thickSum = 0;
	UINT count = 0;
	for (UINT cp = 0; cp < cpCount; cp += sampleStep) {
		thickSum += stroke->getControlPoint(cp).thick;
		count++;
	}
	double averageThick = thickSum / (double)count;

	if (averageThick < 2.0)
		return;
	const double length = stroke->getLength();
	assert(m_level->begin() != m_level->end());
	TLevel::Iterator lit = m_level->begin();
	double s = 0;

	while (s < length) {
		TFrameId fid = lit->first;
		TVectorImageP img = m_level->frame(fid);
		if (++lit == m_level->end())
			lit = m_level->begin();
		assert(img);
		if (img->getType() != TImage::VECTOR)
			return;
		double t = stroke->getParameterAtLength(s);
		TThickPoint p = stroke->getThickPoint(t);
		TPointD v = stroke->getSpeed(t);
		double ang = rad2degree(atan(v)) + m_rotation;
		TRectD bbox = img->getBBox();
		TPointD center = 0.5 * (bbox.getP00() + bbox.getP11());
		//double rx = bbox.getLx() * 0.5;
		double ry = bbox.getLy() * 0.5;
		if (ry * ry < 1e-5)
			ry = p.thick;
		double sc = p.thick / ry;
		if (sc < 0.0001)
			sc = 0.0001;
		TAffine aff =
			TTranslation(p) * TRotation(ang) * TScale(sc) * TTranslation(-center);
		//TVectorRenderData rd2(rd, rd.m_aff * aff);
		//c'era un crash se, dopo aver fatto new color, si selezionava un pattern
		//e si disegnava (stack overflow). Ora r2 prende la palette
		//dell'immagine caricata
		TVectorImage *imgPointer = img.getPointer();

		//TVectorRenderData rd2(rd.m_aff * aff,rd.m_clippingRect,
		//                  img->getPalette(),rd.m_cf,rd.m_antiAliasing);

		flash.pushMatrix();
		flash.multMatrix(aff);
		flash.buildImage(imgPointer, false);
		//flash.draw(imgPointer, 0);
		flash.popMatrix();

		double ds = tmax(2.0, sc * bbox.getLx() + m_space);
		s += ds;
	}
}

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

void TVectorImagePatternStrokeStyle::loadData(TInputStreamInterface &is)
{
	m_level = TLevelP();
	m_name = "";
	string name;
	is >> name >> m_space >> m_rotation;
	if (name != "") {
		try {
			loadLevel(name);
		} catch (...) {
		}
	}
}

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

void TVectorImagePatternStrokeStyle::loadData(int ids, TInputStreamInterface &is)
{
	if (ids != 100)
		throw TException("image pattern stroke style: unknown obsolete format");

	m_level = TLevelP();
	m_name = "";
	string name;
	is >> name;
	if (name != "") {
		try {
			loadLevel(name);
		} catch (...) {
		}
	}
}

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

void TVectorImagePatternStrokeStyle::saveData(TOutputStreamInterface &os) const
{
	os << m_name << m_space << m_rotation;
}

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

TFilePath TVectorImagePatternStrokeStyle::getRootDir()
{
	return m_rootDir;
}

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

TStrokeProp *TVectorImagePatternStrokeStyle::makeStrokeProp(const TStroke *stroke)
{
	return new TVectorImagePatternStrokeProp(stroke, this);
}

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

void TVectorImagePatternStrokeStyle::getObsoleteTagIds(vector<int> &ids) const
{
	//ids.push_back(100);
}

//*************************************************************************************
//    Style declarations  instances
//*************************************************************************************

namespace
{

TColorStyle::Declaration s0(new TCenterLineStrokeStyle());
TColorStyle::Declaration s1(new TSolidColorStyle());
TColorStyle::Declaration s3(new TRasterImagePatternStrokeStyle());
TColorStyle::Declaration s4(new TVectorImagePatternStrokeStyle());
}