Blob Blame Raw


#include <qstringlist.h>

#include "tsimplecolorstyles.h"

#include "ttessellator.h"
#include "trop.h"
#include "drawutil.h"
#include "tflash.h"
#include "tpixelutils.h"
#include "tlevel_io.h"
#include "timage_io.h"
#include "trandom.h"
#include "tvectorimage.h"
#include "toonz/toonzscene.h"
#include "toonz/imagestyles.h"

//*************************************************************************************
//    TTextureStyle  implementation
//*************************************************************************************

namespace
{

TRandom Random;

#define QUAD_PRIMITIVE GL_QUAD_STRIP

typedef std::pair<TPointD, TPointD> PointPair;
typedef std::vector<TPointD> PointArray;

PointPair computeTexParameters(const TPointD &p1,
							   const TPointD &p2,
							   const TPointD &tex1,
							   const TPointD &tex2,
							   const PointPair &newP,
							   const TDimension &size)
{
	// texture points
	static PointPair tex;

	// set vector of movement
	TPointD v = (newP.first + newP.second) * 0.5 - (p2 + p1) * 0.5;

	// compute length of opposite segment
	double lenghtNextP1NextP2 = tdistance(newP.first, newP.second);

	// compute parameter texture offset starting from 0.5
	double texParameterOffset = lenghtNextP1NextP2 / size.lx * 0.5;

	// fix value for s
	tex.first.x = 0.5 - texParameterOffset;
	tex.second.x = 0.5 + texParameterOffset;

	// get length
	double disty = norm(v);
	assert(tex1.y == tex2.y);

	// find parameter for t
	texParameterOffset = disty / size.ly;

	// fix values for t (oldValue + newValue)
	tex.first.y =
		tex.second.y = tex1.y + texParameterOffset;

	return tex;
}

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

void setTexCoords(const TPointD tex, TOutlinePoint &p)
{
	p.u = tex.x;
	p.v = tex.y;
}

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

TPointD getTexCoords(const TOutlinePoint &p)
{
	return TPointD(p.u, p.v);
}

} //namespace

//-----------------------------------------------------------------------------
TTextureStyle::TTextureStyle(const TRasterP &ras, const TFilePath &texturePath)
	: m_texture(ras), m_texturePath(texturePath), m_tessellator(new TglTessellator), m_params(), m_texturePathLoaded()
{
	setAverageColor();
}

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

TFilePath TImageStyle::m_libraryDir;
ToonzScene *TImageStyle::m_currentScene = 0;

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

TTextureStyle::TTextureStyle(const TTextureStyle &other)
	: TOutlineStyle(other), TRasterStyleFx(other), TImageStyle(other), m_texture(other.m_texture), m_texturePath(other.m_texturePath), m_texturePathLoaded(other.m_texturePathLoaded), m_params(other.m_params), m_tessellator(new TglTessellator)
{
	setAverageColor();
}

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

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

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

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

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

QString TTextureStyle::getDescription() const
{
	return "TextureStyle";
}

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

//-----------------------------------------------------------------------------
int TTextureStyle::getTagId() const
{
	return 4;
}
//-----------------------------------------------------------------------------

TRasterP TTextureStyle::getTexture() const
{
	return m_texture;
}
//-----------------------------------------------------------------------------

void TTextureStyle::setAverageColor()
{
	loadTextureRaster();

	TRaster32P r32 = (TRaster32P)m_texture;
	if (!r32) {
		m_averageColor = TPixel::Black;
		return;
	}
	if (m_params.m_isPattern) {
		m_averageColor = m_params.m_patternColor;
		return;
	}
	TPixelD color(0, 0, 0, 0);
	r32->lock();
	for (int i = 0; i < r32->getLy(); i++) {
		TPixel32 *buf = r32->pixels(i);
		for (int j = 0; j < r32->getLx(); j++, buf++) {
			color.r += buf->r;
			color.g += buf->g;
			color.b += buf->b;
			color.m += buf->m;
		}
	}
	r32->unlock();
	int pixels = r32->getLx() * r32->getLy();
	m_averageColor.r = (int)(color.r / pixels);
	m_averageColor.g = (int)(color.g / pixels);
	m_averageColor.b = (int)(color.b / pixels);
	m_averageColor.m = (int)(color.m / pixels);
}

void TTextureStyle::setTexture(const TRasterP &ras)
{
	m_texture = ras;
	delete m_tessellator;
	m_tessellator = new TglTessellator;

	setAverageColor();
}
//-----------------------------------------------------------------------------
TPixel32 TTextureStyle::getAverageColor() const
{
	return m_averageColor;
}
//-----------------------------------------------------------------------------

void TTextureStyle::computeOutline(const TStroke *stroke,
								   TStrokeOutline &outline,
								   TOutlineUtil::OutlineParameter param) const
{
	TOutlineStyle::computeOutline(stroke, outline, param);
	std::vector<TOutlinePoint> &v = outline.getArray();
	PointPair newPnt;
	TDimension size = m_texture->getSize();
	UINT i = 0;
	for (i = 2; i < v.size(); i += 2) {
		newPnt.first = convert(v[i]);
		newPnt.second = convert(v[i + 1]);
		newPnt = computeTexParameters(convert(v[i - 2]), convert(v[i - 1]),
									  getTexCoords(v[i - 2]), getTexCoords(v[i - 1]),
									  newPnt,
									  size);
		setTexCoords(newPnt.first, v[i]);
		setTexCoords(newPnt.second, v[i + 1]);
	}
	for (i = 0; i < v.size(); i++) {
		v[i].u = (i & 1) == 0 ? 0 : 1;
	}
}

//-----------------------------------------------------------------------------
void TTextureStyle::drawStroke(const TColorFunction *cf, TStrokeOutline *outline, const TStroke *stroke) const
{
	/*struct locals {
    static float adaptToGLTextureFunction( USHORT style )
    {
      switch( style )
      {
      case TTextureStyle::DECAL:
        return GL_DECAL;
        break;
      case TTextureStyle::MODULATE:
        return GL_MODULATE;
        break;
      case TTextureStyle::BLEND:
        return GL_BLEND;
        break;
      }
      return GL_DECAL;
    }
  };    // locals;*/

	UINT i;

	std::vector<TOutlinePoint> &v = outline->getArray();
	if (v.empty())
		return;

	const TRasterP texture = m_texture;

	assert(!v.empty() || !texture);

	if (v.empty() || !texture)
		return;

	static const int stride = sizeof(TOutlinePoint);

	glColor4d(1.0, 1.0, 1.0, 1.0);

	// information about vertex
	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(2, GL_DOUBLE, stride, &v[0]);

	glEnable(GL_TEXTURE_2D);

#ifdef _DEBUG
	GLboolean blendEnabled;
	glGetBooleanv(GL_BLEND, &blendEnabled);
	assert(blendEnabled);
#endif

	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glTexCoordPointer(2, GL_DOUBLE, stride, &(v[0].u));

	TextureInfoForGL texInfo;

	m_texture->lock();
	TRasterP texImage = prepareTexture(m_texture, texInfo);

	// Generate a texture id and bind it.
	GLuint texId;
	glGenTextures(1, &texId);

	glBindTexture(GL_TEXTURE_2D, texId);

	// Specify texture parameters
	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);

	glTexEnvf(GL_TEXTURE_ENV,
			  GL_TEXTURE_ENV_MODE,
			  GL_MODULATE); // change texture blending function

	glPixelStorei(GL_UNPACK_ROW_LENGTH,
				  texImage->getWrap() != texImage->getLx() ? texImage->getWrap() : 0);

	if (texImage != m_texture)
		texImage->lock();

	// Load texture
	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());

	m_texture->unlock();
	if (texImage != m_texture)
		texImage->lock();

	// Step 1: draw outline
	glBegin(GL_LINE_STRIP);
	for (i = 0; i < v.size(); i += 2)
		glArrayElement(i);
	glEnd();
	glBegin(GL_LINE_STRIP);
	for (i = 1; i < v.size(); i += 2)
		glArrayElement(i);
	glEnd();

	// Step 2: draw texturized stroke
	/*
  #ifdef MACOSX                   //NON SI CAPISCE PERCHE DRAWARRAY E DISPLAY LIST PROVOCHINO UN CRASH SU MAC
  #warning "INDAGARE ANCORA!!!!"  //PER ADESSO CHIEDO SE STA REGISTANDO UNA DISPLAYLIST E IN QUEL CASO NON USO GLI ARRAY 
  GLuint listId;
  glGetIntegerv(GL_LIST_INDEX,(GLint*)&listId);   
  if(listId==0)
  {
  #endif  
  */
	glDrawArrays(QUAD_PRIMITIVE, 0, v.size());
	/*
  #ifdef MACOSX      
  }
  else
  {
    glBegin( GL_QUAD_STRIP );
    for(UINT i=0; i< v.size(); i++) 
      glVertex2dv( &v[i].x );
    glEnd();
  } 
  #endif
  */

	// Delete texture
	glDeleteTextures(1, &texId);

	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
	glDisable(GL_TEXTURE_2D);
}
//-----------------------------------------------------------------------------
//drawRegion( const TVectorRenderData &rd,  TRegionOutline &boundary ) const
void TTextureStyle::drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const
{
	if (m_tessellator)
		m_tessellator->tessellate(cf, antiAliasing, boundary, m_texture);
}
//-----------------------------------------------------------------------------
void TTextureStyle::drawRegion(TFlash &flash, const TRegion *r) const
{
	flash.setTexture(m_texture);
	flash.setFillStyleMatrix(TAffine());
	flash.drawRegion(*r);
	//rd.setTexture(m_colorStyle->getMainColor());
}
//-----------------------------------------------------------------------------

void TTextureStyle::setFill(TFlash &flash) const
{
	flash.setTexture(getTexture());
}

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

//----------------------------------------
namespace
{

void tileRaster(const TRaster32P &tile, const TRaster32P &rout)
{
	int x0, y0;

	if (rout->getLy() > tile->getLy()) ///tile must be centered in rout
		y0 = tile->getLy() - (((rout->getLy() - tile->getLy()) / 2) % tile->getLy());
	else
		y0 = (tile->getLy() - rout->getLy()) / 2;
	if (rout->getLx() > tile->getLx()) ///tile must be centered in rout
		x0 = tile->getLx() - (((rout->getLx() - tile->getLx()) / 2) % tile->getLx());
	else
		x0 = (tile->getLx() - rout->getLx()) / 2;

	//x0-=tround(offs.x);
	//y0-=tround(offs.y);
	while (x0 < 0)
		x0 += tile->getLx();
	while (y0 < 0)
		y0 += tile->getLy();
	x0 = x0 % tile->getLx();
	y0 = y0 % tile->getLy();

	int y = y0;
	for (int i = 0; i < rout->getLy(); i++, y++) {
		if (y == tile->getLy())
			y = 0;
		int x = x0;
		TPixel32 *pixout = rout->pixels(i);
		TPixel32 *pixin = tile->pixels(y) + x;

		for (int j = 0; j < rout->getLx(); j++, pixin++, pixout++, x++) {
			if (x == tile->getLx()) {
				x = 0, pixin = tile->pixels(y);
			}
			*pixout = *pixin;
		}
	}
}

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

void doContrast(double contrast, const TRaster32P &rin)
{
	TPixel32 *row, *pix;
	int i, j, lx, ly, wrap;
	double line_avg_r, line_avg_g, line_avg_b;
	double avg_r = 0, avg_g = 0, avg_b = 0;

	lx = rin->getLx();
	ly = rin->getLy();
	wrap = rin->getWrap();

	for (j = 0, row = (TPixel32 *)rin->getRawData(); j < ly; j++, row += wrap) {
		line_avg_r = 0;
		line_avg_g = 0;
		line_avg_b = 0;
		for (i = 0, pix = row; i < lx; i++, pix++) {
			line_avg_r += pix->r;
			line_avg_g += pix->g;
			line_avg_b += pix->b;
		}
		avg_r += line_avg_r / lx;
		avg_g += line_avg_g / lx;
		avg_b += line_avg_b / lx;
	}

	avg_r /= ly;
	avg_g /= ly;
	avg_b /= ly;

	int ires;
	double dres;
	TPixel32 lut[256];

	for (int i = 0; i < 256; i++) {
		dres = contrast * (i - avg_r) + avg_r;
		ires = tround(dres);
		lut[i].r = tcrop(ires, 0, 255);
		dres = contrast * (i - avg_g) + avg_g;
		ires = tround(dres);
		lut[i].g = tcrop(ires, 0, 255);
		dres = contrast * (i - avg_b) + avg_b;
		ires = tround(dres);
		lut[i].b = tcrop(ires, 0, 255);
	}

	for (j = 0, row = (TPixel32 *)rin->getRawData(); j < ly; j++, row += wrap) {
		for (i = 0, pix = row; i < lx; i++, pix++) {
			pix->r = lut[pix->r].r;
			pix->g = lut[pix->g].g;
			pix->b = lut[pix->b].b;

			if (pix->r > pix->m)
				pix->r = pix->m;
			if (pix->g > pix->m)
				pix->g = pix->m;
			if (pix->b > pix->m)
				pix->b = pix->m;
		}
	}
}

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

#define BYTE_ROUND(A) ((UINT)((A) + (1U << 23)) >> 24)

#define MAGICFAC (257U * 256U + 1U)

static void doPattern(const TRaster32P &r, TPixel32 color)
{
	int i, j, fac = 0;

	if (TPixelGR8::from(color).value < 128)
		fac = 255 - color.m * (255.0 - color.m) / 255.0;

	TPixel32 *buf;
	UINT b;

	for (i = 0; i < r->getLy(); i++) {
		buf = (TPixel32 *)r->pixels(i);
		for (j = 0; j < r->getLx(); j++, buf++) {
			if (buf->m > 0) {
				b = TPixelGR8::from(*buf).value * MAGICFAC;

				buf->r = BYTE_ROUND(b * color.r + fac);
				buf->g = BYTE_ROUND(b * color.g + fac);
				buf->b = BYTE_ROUND(b * color.b + fac);
				if (buf->m < 255 || color.m < 255) {
					buf->m = BYTE_ROUND(buf->m * color.m / 255.0);
					premult(*buf);
				} else
					buf->m = 255;
			}
		}
	}
}

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

void applyTexture(const TRaster32P &rTex, const TRaster32P &r, TPoint p)
{
	while (p.x < 0)
		p.x += rTex->getLx();
	while (p.y < 0)
		p.y += rTex->getLy();
	r->lock();
	rTex->lock();
	for (int y = 0, yTex = p.y; y < r->getLy(); y++, yTex++) {
		TPixel32 *pixIn = r->pixels(y);
		if (yTex >= rTex->getLy())
			yTex -= rTex->getLy();

		TPixel32 *pixTex = (TPixel32 *)(rTex->pixels(yTex)) + p.x;

		for (int x = 0, xTex = p.x; x < r->getLx(); x++, xTex++, pixIn++, pixTex++) {
			if (xTex >= rTex->getLx())
				xTex -= rTex->getLx(), pixTex -= rTex->getLx();

			if (pixIn->m == 0)
				continue;

			if (pixIn->m == 255)
				*pixIn = *pixTex;
			else {
				pixIn->r = pixIn->m * pixTex->r / 255;
				pixIn->g = pixIn->m * pixTex->g / 255;
				pixIn->b = pixIn->m * pixTex->b / 255;
				pixIn->m = pixIn->m * pixTex->m / 255;
			}
		}
	}
	r->unlock();
	rTex->unlock();
}

} //namespace
//---------------------------------------------------------------------------------------------------------------

TPoint computeCentroid(const TRaster32P &r);

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

int TTextureStyle::getParamCount() const
{
	return 8;
}

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

QString TTextureStyle::getParamNames(int index) const
{
	switch (index) {
	case 0: return "Use As Pattern";
	case 1: return "Position";
	case 2: return "Scale";
	case 3: return "Rotation(degrees)";
	case 4: return "X displ";
	case 5: return "Y displ";
	case 6: return "Contrast";
	case 7: return "Load From File";
	default:
		assert(false);
	}

	return QString("");
}

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

void TTextureStyle::getParamRange(int index, double &min, double &max) const
{
	assert(index > 1);
	switch (index) {
	case 2:
		min = 0.15;
		max = 10;
		break;
	case 3:
		min = -180;
		max = 180;
		break;
	case 4:
		min = -500;
		max = 500;
		break;
	case 5:
		min = -500;
		max = 500;
		break;
	case 6:
		min = 0.01;
		max = 10;
		break;
	default:
		assert(false);
	}
}

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

TColorStyle::ParamType TTextureStyle::getParamType(int index) const
{
	assert(0 <= index && index < getParamCount());
	if (index == 0)
		return TColorStyle::BOOL;
	else if (index == 1)
		return TColorStyle::ENUM;
	else if (index == 7)
		return TColorStyle::FILEPATH;
	return TColorStyle::DOUBLE;
}

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

void TTextureStyle::getParamRange(int index, QStringList &enumItems) const
{
	assert(index == 1 || index == 7);
	if (index == 1)
		enumItems << "FIXED"
				  << "AUTOMATIC"
				  << "RANDOM";
	else if (index == 7)
		enumItems << "bmp"
				  << "jpg"
				  << "png"
				  << "tif"
				  << "tiff"
				  << "gif";
}

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

bool TTextureStyle::getParamValue(TColorStyle::bool_tag, int index) const
{
	assert(index == 0);
	return m_params.m_isPattern;
}

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

double TTextureStyle::getParamValue(TColorStyle::double_tag, int index) const
{
	assert(index > 1);
	switch (index) {
	case 2: return m_params.m_scale;
	case 3: return m_params.m_rotation;
	case 4: return m_params.m_displacement.x;
	case 5: return m_params.m_displacement.y;
	case 6: return m_params.m_contrast;
	default:
		assert(false);
	}
	return 0;
}

TFilePath TTextureStyle::getParamValue(TColorStyle::TFilePath_tag, int index) const
{
	assert(index == 7);
	return m_texturePath;
}

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

int TTextureStyle::getParamValue(TColorStyle::int_tag, int index) const
{
	assert(index == 1);

	return m_params.m_type;
}

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

void TTextureStyle::setParamValue(int index, const TFilePath &value)
{
	assert(index == 7);
	m_texturePath = value;
	setAverageColor();
}

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

void TTextureStyle::setParamValue(int index, int value)
{
	assert(index == 1);

	m_params.m_type = (TTextureParams::TYPE)value;
}

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

void TTextureStyle::setParamValue(int index, bool value)
{
	assert(index == 0);
	m_params.m_isPattern = value;
}

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

void TTextureStyle::setParamValue(int index, double value)
{
	assert(index > 1);
	switch (index) {
	case 0:
		m_params.m_isPattern = (((int)value == 0) ? false : true);
		break;
	case 1:
		m_params.m_type = (((int)value == 0) ? TTextureParams::FIXED : ((int)value == 1) ? TTextureParams::AUTOMATIC : TTextureParams::RANDOM);
		break;
	case 2:
		m_params.m_scale = value;
		break;
	case 3:
		m_params.m_rotation = value;
		break;
	case 4:
		m_params.m_displacement.x = value;
		break;
	case 5:
		m_params.m_displacement.y = value;
		break;
	case 6:
		m_params.m_contrast = value;
		break;
	default:
		assert(false);
	}
}

//------------------------------------------------------------
bool TTextureStyle::loadTextureRaster()
{
	if (m_texturePathLoaded != TFilePath() && m_texturePath == m_texturePathLoaded)
		return true;

	m_texturePathLoaded = m_texturePath;

	TFilePath path;
	if (m_texturePath.getParentDir() != TFilePath()) //It's a custom texture
	{
		assert(m_currentScene);
		path = m_currentScene->decodeFilePath(m_texturePath);
		if (path.isLevelName()) {
			TLevelReader lr(path);
			path = path.withFrame(lr.loadInfo()->begin()->first);
		}
	} else //is a library texture
	{
		path = m_texturePath.withParentDir(m_libraryDir + "textures");
	}

	TRasterP aux;

	if (!TImageReader::load(path, aux)) {
		m_texture = TRaster32P(128, 128);
		m_texture->clear();
		m_texturePathLoaded = TFilePath();
		return false;
	}

	m_texture = aux;
	return true;
}

//----------------------------------------------------------------------------
TRaster32P TTextureStyle::loadTextureRasterWithFrame(int frame) const
{
	if (m_texturePathLoaded != TFilePath() && m_texturePath == m_texturePathLoaded && (!m_texturePath.isLevelName() || frame == 0))
		return m_texture->clone();

	TFilePath path;
	if (m_texturePath.getParentDir() != TFilePath()) //It's a custom texture
	{
		assert(m_currentScene);
		path = m_currentScene->decodeFilePath(m_texturePath);
		if (path.isLevelName()) {
			TLevelReader lr(path);
			TLevelP info = lr.loadInfo();
			TLevel::Iterator it = info->begin();
			//frame = frame % (lr.loadInfo()->getFrameCount());
			std::advance(it, frame % (info->getFrameCount()));

			//
			path = path.withFrame(it->first);
		}
	} else //is a library texture
	{
		path = m_texturePath.withParentDir(m_libraryDir + "textures");
	}

	TRasterP aux;

	if (!TImageReader::load(path, aux)) {
		aux = TRaster32P(128, 128);
		aux->clear();
	}
	return aux;
}

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

void TTextureStyle::loadData(TInputStreamInterface &is)
{

	if (is.versionNumber().first < 71) {
		is >> m_texture;
		setTexture(m_texture);
		return;
	}

	std::string path;
	is >> path;
	m_texturePath = TFilePath(path);

	//TOutlineStyle::loadData(is);
	//is >> m_texture;
	loadTextureRaster();

	is >> m_params.m_patternColor;
	m_averageColor = m_params.m_patternColor;

	double value;

	is >> value;
	m_params.m_isPattern = value == 1.0 ? true : false;

	is >> value;
	m_params.m_type = (((int)value == 0) ? TTextureParams::FIXED : ((int)value == 1) ? TTextureParams::AUTOMATIC : TTextureParams::RANDOM);

	is >> m_params.m_scale;
	is >> m_params.m_rotation;
	is >> m_params.m_displacement.x;
	is >> m_params.m_displacement.y;
	is >> m_params.m_contrast;

	delete m_tessellator;
	m_tessellator = new TglTessellator;

	setAverageColor();
}

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

void TTextureStyle::saveData(TOutputStreamInterface &os) const
{
	//TOutlineStyle::saveData(os);
	//os << m_texture;
	std::wstring wstr = m_texturePath.getWideString();
	std::string str;
	str.assign(wstr.begin(), wstr.end());
	os << str;

	os << m_params.m_patternColor;

	os << ((double)m_params.m_isPattern);

	double value = (m_params.m_type == TTextureParams::FIXED) ? 0 : ((m_params.m_type == TTextureParams::AUTOMATIC) ? 1 : 2);
	os << value;

	os << m_params.m_scale;
	os << m_params.m_rotation;
	os << m_params.m_displacement.x;
	os << m_params.m_displacement.y;
	os << m_params.m_contrast;
}

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

int TTextureStyle::getColorParamCount() const
{
	return 1;
}

TPixel32 TTextureStyle::getColorParamValue(int index) const
{
	return m_params.m_patternColor;
}

void TTextureStyle::setColorParamValue(int index, const TPixel32 &color)
{
	m_params.m_patternColor = color;
}
//----------------------------------------------------------------------------
inline void drawdot(TPixel32 *pix, int wrap)
{
	for (int i = -wrap; i <= wrap; i += wrap)
		for (int j = -1; j <= 1; j++)
			*(pix + i + j) = TPixel::Red;
}

void TTextureStyle::fillCustomTextureIcon(const TRaster32P &r)
{
	r->fill(TPixel::White);
	int x1, x2, x3;
	x2 = r->getLx() / 2;
	x1 = x2 + ((r->getLx() <= 64) ? 6 : 9);
	x3 = x2 - ((r->getLx() <= 64) ? 6 : 9);

	TPixel32 *pix = r->pixels(r->getLy() / 4);
	drawdot(pix + x1, r->getWrap());
	drawdot(pix + x2, r->getWrap());
	drawdot(pix + x3, r->getWrap());
	// TImageWriter::save(TFilePath("C:\\temp\\boh.png"), r);
	/**(pix+x1) = TPixel::Black;
*(pix+x2) = TPixel::Black;
*(pix+x3) = TPixel::Black;*/
}

void TTextureStyle::makeIcon(const TDimension &outputRect)
{

	if (!m_icon || m_icon->getSize() != outputRect) {
		TRaster32P ras(outputRect);
		ras->fill(TPixel::Red);
		m_icon = ras;
	}

	if (!loadTextureRaster()) {
		fillCustomTextureIcon(m_icon);
		//m_icon->fill(TPixel::Green);
		return;
	}
	TRaster32P rTex;

	if (m_params.m_contrast != 1.0 || m_params.m_isPattern) {
		rTex = m_texture->clone();

		if (m_params.m_contrast != 1.0)
			doContrast(m_params.m_contrast, rTex);

		if (m_params.m_isPattern)
			doPattern(rTex, m_params.m_patternColor);
	} else
		rTex = m_texture;

	double fitScale = std::min((double)(outputRect.lx) / m_texture->getLx(), (double)(outputRect.ly) / m_texture->getLy());
	TAffine affine = TScale(m_params.m_scale * (fitScale)) * TRotation(-m_params.m_rotation);

	if (affine != TAffine()) {
		int border = 2;
		TRaster32P raux(m_icon->getLx() + 2 * border, m_icon->getLy() + 2 * border);
		TRaster32P rin(convert(affine.inv() * TRectD(0, 0, raux->getLx() - 1, raux->getLy() - 1)).getSize());
		tileRaster(rTex, rin);

		TRop::resample(raux, rin, affine.place(rin->getCenterD(), raux->getCenterD()));

		TRop::copy(m_icon, raux->extract(border, border, m_icon->getLx() + border - 1, m_icon->getLy() + border - 1));
	} else
		applyTexture(rTex, m_icon, TPoint());
}

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

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

bool TTextureStyle::doCompute(const Params &params) const
{
	TRaster32P rTex = loadTextureRasterWithFrame(params.m_frame);
	TRaster32P r = params.m_r;
	//TRaster32P rTex = m_texture->clone();

	assert(r);

	if (m_params.m_contrast != 1.0)
		doContrast(m_params.m_contrast, rTex);

	if (m_params.m_isPattern)
		doPattern(rTex, m_params.m_patternColor);

	TPoint p = -convert(m_params.m_displacement);

	if (m_params.m_type == TTextureParams::FIXED)
		p += params.m_p;
	else if (m_params.m_type == TTextureParams::RANDOM)
		p += TPoint(Random.getUInt(m_texture->getLx()), Random.getUInt(m_texture->getLy()));

	if (m_params.m_scale != 1.0 || m_params.m_rotation != 0.0) {
		TAffine affine = TScale(m_params.m_scale) * TRotation(-m_params.m_rotation);

		if (m_params.m_type == TTextureParams::AUTOMATIC) {
			TRect bBox;
			TRop::computeBBox(r, bBox);

			r = r->extract(bBox);
			p += r->getCenter() - computeCentroid(r);
		}

		p.x = (p.x % m_texture->getLx());
		p.y = (p.y % m_texture->getLy());

		TRect rectIn = convert(affine.inv() * TRectD(0, 0, r->getLx() - 1, r->getLy() - 1));
		TRaster32P rin(rectIn.getLx() + abs(p.x), rectIn.getLy() + abs(p.y));
		TRaster32P rout(r->getSize());

		tileRaster(rTex, rin);
		TRop::resample(rout, rin, affine.place(rin->getCenterD(), rout->getCenterD() - convert(p)));
		rTex = rout;
		p = TPoint();
	} else {
		if (m_params.m_type == TTextureParams::AUTOMATIC)
			p += rTex->getCenter() - computeCentroid(r);
		p.x = (p.x % m_texture->getLx());
		p.y = (p.y % m_texture->getLy());
	}

	applyTexture(rTex, r, p);

	return true;
}

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

namespace
{

TRaster32P makeSimpleRaster()
{
	TRaster32P ras(2, 2);
	ras->fill(TPixel32::White);
	return ras;
}

TColorStyle::Declaration s2(new TTextureStyle(makeSimpleRaster(), TFilePath()));
}