/*
For general Scribus (>=1.3.2) copyright and licensing information please refer
to the COPYING file provided with the program. Following this notice may exist
a copyright and/or license notice that predates the release of Scribus 1.3.2
for which a new license (GPL+exception) is in place.
*/
#include "scconfig.h"
#include "colormgmt/sccolormgmtengine.h"
#include "scimgdataloader_psd.h"
#include "util_color.h"
#include "sccolorengine.h"
#include "scribuscore.h"

#include <cstdint>
#include <QFile>
#include <QFileInfo>
#include <QList>

static QDataStream & operator>> ( QDataStream & s, PSDHeader & header )
{
	s >> header.signature;
	s >> header.version;
	for (int i = 0; i < 6; i++)
	{
		s >> header.reserved[i];
	}
	s >> header.channel_count;
	s >> header.height;
	s >> header.width;
	s >> header.depth;
	s >> header.color_mode;
	return s;
}

ScImgDataLoader_PSD::ScImgDataLoader_PSD()
{
	initSupportedFormatList();
}

void ScImgDataLoader_PSD::initSupportedFormatList()
{
	m_supportedFormats.clear();
	m_supportedFormats.append( "psd" );
}

void ScImgDataLoader_PSD::loadEmbeddedProfile(const QString& fn, int /*page*/)
{
	m_embeddedProfile.resize(0);
	m_profileComponents = 0;

	ScColorProfile prof;
	ScColorMgmtEngine engine(ScCore->defaultEngine);
	QFileInfo fi(fn);
	if (!fi.exists())
		return;

	QString ext = fi.suffix().toLower();
	if (ext != "psd")
		return;

	QFile f(fn);
	if (!f.open(QIODevice::ReadOnly))
		return;

	m_imageInfoRecord.xres = 72;
	m_imageInfoRecord.yres = 72;
	QDataStream s( &f );
	s.setByteOrder( QDataStream::BigEndian );
	PSDHeader header;
	s >> header;
	// Check image file format.
	if (s.atEnd() || !IsValid(header))
		return;
	// Check if it's a supported format.
	if (!IsSupported(header))
		return;
	if (!LoadPSDResources(s, header, s.device()->pos()))
		return;
	if (m_embeddedProfile.size() > 0)
	{
		prof = engine.openProfileFromMem(m_embeddedProfile.data());
		if (prof)
		{
			if (prof.colorSpace() == ColorSpace_Rgb)
				m_profileComponents = 3;
			if (prof.colorSpace() == ColorSpace_Cmyk)
				m_profileComponents = 4;
			if (prof.colorSpace() == ColorSpace_Gray)
				m_profileComponents = 1;
		}
	}
	f.close();
}

bool ScImgDataLoader_PSD::preloadAlphaChannel(const QString& fn, int /*page*/, int res, bool& hasAlpha)
{
	bool valid = m_imageInfoRecord.isRequest;
	QMap<int, ImageLoadRequest> req = m_imageInfoRecord.RequestProps;
	initialize();
	hasAlpha = false;
	m_imageInfoRecord.RequestProps = req;
	m_imageInfoRecord.isRequest = valid;
	QFileInfo fi(fn);
	if (!fi.exists())
		return false;

	QFile f(fn);
	if (f.open(QIODevice::ReadOnly))
	{
		QDataStream s( &f );
		s.setByteOrder( QDataStream::BigEndian );
		PSDHeader header;
		s >> header;
		// Check image file format.
		if (s.atEnd() || !IsValid(header))
			return false;
		// Check if it's a supported format.
		if (!IsSupported(header))
			return false;
		if (!LoadPSD(s, header))
			r_image.resize(0);
		f.close();
		m_imageInfoRecord.valid = true;
		if (header.color_mode == CM_CMYK)
			m_imageInfoRecord.valid = hasAlpha = (m_maxChannels > 4);
		else if (header.color_mode == CM_GRAYSCALE)
			m_imageInfoRecord.valid = hasAlpha = (m_maxChannels > 1);
		else
			m_imageInfoRecord.valid = hasAlpha = (m_maxChannels >= 4);
		return true;
	}
	return false;
}

bool ScImgDataLoader_PSD::loadPicture(const QString& fn, int /*page*/, int res, bool thumbnail)
{
	ScColorMgmtEngine engine(ScCore->defaultEngine);
	bool isCMYK = false;
	if (!QFile::exists(fn))
		return false;
	bool valid = m_imageInfoRecord.isRequest;
	QMap<int, ImageLoadRequest> req = m_imageInfoRecord.RequestProps;
	initialize();
	m_imageInfoRecord.RequestProps = req;
	m_imageInfoRecord.isRequest = valid;
	m_imageInfoRecord.type = ImageTypePSD;
	m_imageInfoRecord.exifDataValid = false;
	m_imageInfoRecord.layerInfo.clear();
	m_imageInfoRecord.PDSpathData.clear();
	QFile f(fn);
	if (f.open(QIODevice::ReadOnly))
	{
		m_imageInfoRecord.xres = 72;
		m_imageInfoRecord.yres = 72;
		QDataStream s( &f );
		s.setByteOrder( QDataStream::BigEndian );
		PSDHeader header;
		s >> header;
		// Check image file format.
		if (s.atEnd() || !IsValid(header))
			return false;
		// Check if it's a supported format.
		if (!IsSupported(header))
			return false;
		if (thumbnail)
			header.reserved[0] = 't';
		if (!LoadPSD(s, header))
			return false;
		if (m_embeddedProfile.size() > 0)
		{
			ScColorProfile prof = engine.openProfileFromMem(m_embeddedProfile);
			m_imageInfoRecord.profileName = prof.productDescription();
			m_imageInfoRecord.embeddedProfileName = m_imageInfoRecord.profileName;
			m_imageInfoRecord.isEmbedded = true;
		}
		isCMYK = (header.color_mode == CM_CMYK);
		if (header.color_mode == CM_CMYK)
			m_imageInfoRecord.colorspace = ColorSpaceCMYK;
		else if (header.color_mode == CM_RGB)
			m_imageInfoRecord.colorspace = ColorSpaceRGB;
		else if (header.color_mode == CM_GRAYSCALE)
			m_imageInfoRecord.colorspace = ColorSpaceGray;
		else if (header.color_mode == CM_DUOTONE)
			m_imageInfoRecord.colorspace = ColorSpaceDuotone;
		f.close();
		if (thumbnail)
		{
			if (!m_imageInfoRecord.exifInfo.thumbnail.isNull())
			{
				if (isCMYK)
				{
					r_image.create(m_imageInfoRecord.exifInfo.thumbnail.width(), m_imageInfoRecord.exifInfo.thumbnail.height(), 5);
					m_pixelFormat = Format_CMYKA_8;
				}
				else
				{
					r_image.create(m_imageInfoRecord.exifInfo.thumbnail.width(), m_imageInfoRecord.exifInfo.thumbnail.height(), 4);
					m_pixelFormat = Format_RGBA_8;
				}
				QRgb *s;
				unsigned char cc, cm, cy, ck;
				uchar *d;
				for (int yit = 0; yit < m_imageInfoRecord.exifInfo.thumbnail.height(); ++yit)
				{
					s = (QRgb*)(m_imageInfoRecord.exifInfo.thumbnail.scanLine( yit ));
					d = r_image.scanLine( yit );
					for (int xit = 0; xit < m_imageInfoRecord.exifInfo.thumbnail.width(); ++xit)
					{
						if (isCMYK)
						{
							cc = 255 - qRed(*s);
							cm = 255 - qGreen(*s);
							cy = 255 - qBlue(*s);
							ck = qMin(qMin(cc, cm), cy);
							d[0] = cc-ck;
							d[1] = cm-ck;
							d[2] = cy-ck;
							d[3] = ck;
							d[4] = 255;
						}
						else
						{
							d[0] = qRed(*s);
							d[1] = qGreen(*s);
							d[2] = qBlue(*s);
							d[3] = 255;
						}
						s++;
						d += r_image.channels();
					}
				}
				m_imageInfoRecord.exifInfo.width = header.width;
				m_imageInfoRecord.exifInfo.height = header.height;
				return true;
			}
		}
		m_imageInfoRecord.exifInfo.width = header.width;
		m_imageInfoRecord.exifInfo.height = header.height;
		m_imageInfoRecord.BBoxX = 0;
		m_imageInfoRecord.BBoxH = r_image.height();
		return true;
	}
	return false;
}

bool ScImgDataLoader_PSD::LoadPSD(QDataStream& s, const PSDHeader& header)
{
	// Create dst image.
	m_imageInfoRecord.valid = false;
	if (header.color_mode == CM_CMYK)
	{
		if (!r_image.create(header.width, header.height, 5))
			return false;
		m_pixelFormat = Format_CMYKA_8;
	}
	else
	{
		if (!r_image.create(header.width, header.height, 4))
			return false;
		m_pixelFormat = Format_RGBA_8;
	}
	r_image.fill(0);
	m_maxChannels = header.channel_count;

	uint tmp;
	uint resourceDataLen;
	qint64 cresStart;
	qint64 cdataStart;
	qint64 startResource;

	cresStart = s.device()->pos();
	// Skip mode data. FIX: this is incorrect, it's the Colormap Data for indexed Images
	s >> tmp;
	cdataStart = s.device()->pos();

	LoadPSDResources(s, header, cresStart); 
	
	s.device()->seek(cdataStart + tmp);
	s >> resourceDataLen;
	startResource = s.device()->pos();

	if  ((!m_imageInfoRecord.exifInfo.thumbnail.isNull()) && (header.reserved[0] == 't'))
		return true;
	bool ret = LoadPSDImgData(s, header, startResource + resourceDataLen);
	return ret;
}

bool ScImgDataLoader_PSD::LoadPSDResources(QDataStream& s, const PSDHeader& header, qint64 dataOffset)
{
	// Create dst image.
	m_imageInfoRecord.valid = false;

	uint tmp;
	uint resourceDataLen;
	qint64 cdataStart;

	s.device()->seek(dataOffset);

	srand(314159265);
	for (int i = 0; i < 4096; i++)
		m_random_table[i] = rand();
	int tmpd;
	int swap;
	for (int i = 0; i < 4096; i++)
	{
		swap = i + rand() % (4096 - i);
		tmpd = m_random_table[i];
		m_random_table[i] = m_random_table[swap];
		m_random_table[swap] = tmpd;
	}
	// Skip mode data. FIX: this is incorrect, it's the Colormap Data for indexed Images
	s >> tmp;
	cdataStart = s.device()->pos();
	if (tmp != 0)
	{
		if (header.color_mode == CM_DUOTONE)
		{
			short signature;
			short count;
			unsigned short c, m, y, k;
			uchar hc, sc, bc;
			ScColor col;
			s >> signature;
			s >> count;
			qint64 duodataStart = s.device()->pos();
			bool specialColour = false;
			for (int cda = 0; cda < count; cda++)
			{
				s >> signature;
				s >> c >> m >> y >> k;
				switch (signature)
				{
					case 0:	// RGB colour
						col.setRgbColor(c >> 8, m >> 8, y >> 8);
						m_colorTableSc.append(col);
						break;
					case 1:	// HSB colour
						hc = c >> 8;
						sc = m >> 8;
						bc = y >> 8;
						HSVTORGB(hc, sc, bc);
						col.setRgbColor(hc, sc, bc);
						m_colorTableSc.append(col);
						break;
					case 2:	// CMYK colour
						col.setColor(c >> 8, m >> 8, y >> 8, k >> 8);
						m_colorTableSc.append(col);
						break;
					case 3:	// Pantone
					case 4:	// Focoltone
					case 5:	// Truematch
					case 6:	// Toyo 88 colorfinder 1050
					case 7:	// LAB colour space
					case 10: // HKS colors
						if (m_colorTableSc.count() == 0)
							m_colorTableSc.append(ScColor(0, 0, 0, 255));
						else
							m_colorTableSc.append(ScColor(0, 0, 0, 0));
						specialColour = true;
						break;
					case 8: // Grayscale
						c = qRound((c / 10000.0) * 255);
						m_colorTableSc.append(ScColor(0, 0, 0, c));
						break;
				}
			}
			if (specialColour) // we will only load the grayscale image data and do the colorizing with the Image Effects;
			{
				s.device()->seek( duodataStart + 40 );
				qint64 duoNameStart = s.device()->pos();
				qint64 duoCurveStart = duoNameStart + 256;
				for (int cda = 0; cda < count; cda++)
				{
					QString colName;
					s.device()->seek(duoNameStart + 64 * static_cast<qint64>(cda));
					colName = getPascalString(s);
					s.device()->seek(duoCurveStart + 28 * static_cast<qint64>(cda));
					FPointArray tmcu;
					for (int cu = 0; cu < 13; cu++)
					{
						short val;
						s >> val;
						if (val != -1)
							tmcu.addPoint(cu * 1.0 / 12.0, val / 1000.0);
					}
					PSDDuotone_Color colSpec;
					colSpec.Name = colName;
					colSpec.Color = m_colorTableSc[cda];
					colSpec.Curve = tmcu;
					m_imageInfoRecord.duotoneColors.append(colSpec);
				}
				// Initialize loading curve tables with default values
				m_curveTable1.resize(256);
				m_curveTable2.resize(256);
				m_curveTable3.resize(256);
				m_curveTable4.resize(256);
				for (int x = 0 ; x < 256 ; x++)
				{
					m_curveTable1[x] = x;
					m_curveTable2[x] = x;
					m_curveTable3[x] = x;
					m_curveTable4[x] = x;
				}
			}
			else
			{
				s.device()->seek( duodataStart + 40 );
				qint64 duoNameStart = s.device()->pos();
				qint64 duoCurveStart = duoNameStart + 256;
				for (int cda = 0; cda < count; cda++)
				{
					s.device()->seek(duoCurveStart + 28 * static_cast<qint64>(cda));
					FPointArray tmcu;
					for (int cu = 0; cu < 13; cu++)
					{
						short val;
						s >> val;
						if (val != -1)
							tmcu.addPoint(cu * 1.0 / 12.0, val / 1000.0);
					}
					if (cda == 0)
					{
						m_curveTable1.resize(256);
						for (int x = 0 ; x < 256 ; x++)
						{
							m_curveTable1[x] = qMin(255, qMax(0, qRound(getCurveYValue(tmcu, x / 255.0) * 255)));
						}
					}
					else if (cda == 1)
					{
						m_curveTable2.resize(256);
						for (int x = 0 ; x < 256 ; x++)
						{
							m_curveTable2[x] = qMin(255, qMax(0, qRound(getCurveYValue(tmcu, x / 255.0) * 255)));
						}
					}
					else if (cda == 2)
					{
						m_curveTable3.resize(256);
						for (int x = 0 ; x < 256 ; x++)
						{
							m_curveTable3[x] = qMin(255, qMax(0, qRound(getCurveYValue(tmcu, x / 255.0) * 255)));
						}
					}
					else if (cda == 3)
					{
						m_curveTable4.resize(256);
						for (int x = 0 ; x < 256 ; x++)
						{
							m_curveTable4[x] = qMin(255, qMax(0, qRound(getCurveYValue(tmcu, x / 255.0) * 255)));
						}
					}
				}
			}
		}
		else
		{
			QList<uchar> colorTableR;
			QList<uchar> colorTableG;
			QList<uchar> colorTableB;
			colorTableR.reserve(256);
			colorTableG.reserve(256);
			colorTableB.reserve(256);
			m_colorTable.clear();
			m_colorTable.reserve(256);
			uchar r;
			for (uint cc = 0; cc < 256; cc++)
			{
				s >> r;
				colorTableR.append(r);
			}
			for (uint cc = 0; cc < 256; cc++)
			{
				s >> r;
				colorTableG.append(r);
			}
			for (uint cc = 0; cc < 256; cc++)
			{
				s >> r;
				colorTableB.append(r);
			}
			for (uint cc = 0; cc < 256; cc++)
			{
				m_colorTable.append(qRgb(colorTableR[cc], colorTableG[cc], colorTableB[cc]));
			}
		}
	}
	s.device()->seek(cdataStart + tmp);
	s >> resourceDataLen;
	if (resourceDataLen != 0)
		parseResourceData(s, header, resourceDataLen);
	return true;
}

bool ScImgDataLoader_PSD::LoadPSDImgData(QDataStream & s, const PSDHeader & header, qint64 dataOffset)
{
	uint layerDataLen;
	qint64 startLayers;
	s.device()->seek(dataOffset);
	// Skip the reserved data. FIX: Also incorrect, this is the actual Layer Data for Images with Layers
	s >> layerDataLen;
	startLayers = s.device()->pos();
	if (layerDataLen != 0)
	{
		bool re = parseLayer(s, header);
		if (re)
		{
			m_imageInfoRecord.valid = true;
			return re;
		}
		// Try to decode simple psd file, no layers
		s.device()->seek(startLayers + layerDataLen);
		if (s.atEnd())
			return false;
		return loadLayer(s, header);
	}
	// Decoding simple psd file, no layers
	s.device()->seek(s.device()->pos() + layerDataLen);
	loadLayer(s, header);
	return true;
}

bool ScImgDataLoader_PSD::parseLayer(QDataStream& s, const PSDHeader& header)
{
	uint addRes, layerinfo, channelLen, signature, extradata, layermasksize, layerRange, dummy;
	int top, left, bottom, right;
	short numLayers, numChannels;
	short channelType;
	uchar blendKey[4];
	uchar opacity, clipping, flags, filler;
	QString blend;
	struct PSDLayer lay;
	s >> layerinfo;
	s >> numLayers;
	if (numLayers < 0)
		numLayers = -numLayers;
	if (numLayers != 0)
	{
		for (int layer = 0; layer < numLayers; layer++)
		{
			s >> top;
			lay.ypos = top;
			s >> left;
			lay.xpos = left;
			s >> bottom;
			lay.height = bottom - top;
			s >> right;
			lay.width = right - left;
			s >> numChannels;
			// Qt4 check these conversions
			m_maxChannels = qMax(m_maxChannels, (int)numChannels);
			if (numChannels > 6)	// we don't support images with more than 6 channels yet
			{
				m_imageInfoRecord.layerInfo.clear();
				return false;
			}
			lay.channelType.clear();
			lay.channelLen.clear();
			for (int channels = 0; channels < numChannels; channels++)
			{
				s >> channelType;
				s >> channelLen;
				lay.channelType.append(channelType);
				lay.channelLen.append(channelLen);
			}
			s >> signature;
			blend = "";
			for (int i = 0; i < 4; i++)
			{
				s >> blendKey[i];
				blend += QChar(blendKey[i]);
			}
			lay.blend = blend;
			s >> opacity;
			lay.opacity = opacity;
			s >> clipping;
			lay.clipping = clipping;
			s >> flags;
			if (flags & 8)
			{
				if (flags & 16)	// Unknown combination of layer flags, probably an adjustment or effects layer
				{
					m_imageInfoRecord.layerInfo.clear();
					return false;
				}
			}
			lay.flags = flags;
			s >> filler;
			s >> extradata;
			s >> layermasksize;
			lay.maskYpos = 0;
			lay.maskXpos = 0;
			lay.maskHeight = 0;
			lay.maskWidth = 0;
			if (layermasksize != 0)
			{
				s >> lay.maskYpos;
				s >> lay.maskXpos;
				s >> dummy;
				lay.maskHeight = dummy - lay.maskYpos;
				s >> dummy;
				lay.maskWidth = dummy - lay.maskXpos;
				s >> dummy;
			}
			s >> layerRange;
			s.device()->seek( s.device()->pos() + layerRange );
			lay.layerName = getLayerString(s);
			m_imageInfoRecord.layerInfo.append(lay);
			s >> signature;
			if (signature == 0x3842494D)
			{
				while (signature == 0x3842494D )
				{
					s >> signature;
					s >> addRes;
					s.device()->seek( s.device()->pos() + addRes );
					s >> signature;
				}
				s.device()->seek( s.device()->pos() - 4 );
			}
			else
			{
				s.device()->seek( s.device()->pos() - 2 );
				s >> signature;
				if (signature == 0x3842494D)
				{
					while (signature == 0x3842494D )
					{
						s >> signature;
						s >> addRes;
						s.device()->seek( s.device()->pos() + addRes );
						s >> signature;
					}
					s.device()->seek( s.device()->pos() - 4 );
				}
				else
					s.device()->seek( s.device()->pos() - 6 );
			}
		}
		bool firstLayer = true;
		for (int layer = 0; layer < numLayers; layer++)
		{
			loadLayerChannels( s, header, m_imageInfoRecord.layerInfo, layer, &firstLayer );
		}
	}
	else
	{
		s >> numLayers;
		if (numLayers == 0)
			return false;
		loadLayer( s, header);
	}
	return true;
}

bool ScImgDataLoader_PSD::loadChannel(QDataStream& s, const PSDHeader& header, QList<PSDLayer>& layerInfo, uint layer, int channel, int component, RawImage& tmpImg)
{
	qint64 base = s.device()->pos();
	uchar cbyte;
	ushort compression;
	s >> compression;
	if (compression > 1)
		return false;
	if (compression == 0)
	{
		int count = layerInfo[layer].channelLen[channel]-2;
		uchar *ptr;
		for (int i = 0; i < tmpImg.height(); i++)
		{
			ptr =  tmpImg.scanLine(i);
			for (int j = 0; j < tmpImg.width(); j++)
			{
				s >> cbyte;
				count--;
				if ((header.color_mode == CM_CMYK) && (component < 4))
					cbyte = 255 - cbyte;
				if ((header.color_mode == CM_GRAYSCALE) && (component != 3))
				{
					ptr[0] = cbyte;
					ptr[1] = cbyte;
					ptr[2] = cbyte;
				}
				else if ((header.color_mode == CM_DUOTONE) && (component != 3))
				{
					ptr -= component;
					putDuotone(ptr, cbyte);
					ptr += component;
				}
				else if ((header.color_mode == CM_INDEXED) && (component != 3))
				{
					int ccol = m_colorTable[cbyte];
					ptr[0] = qRed(ccol);
					ptr[1] = qGreen(ccol);
					ptr[2] = qBlue(ccol);
				}
				else
					ptr[component] = cbyte;
				if (count == 0)
					break;
				ptr += tmpImg.channels();
			}
			if (count == 0)
				break;
		}
	}
	else
	{
		s.device()->seek(s.device()->pos() + tmpImg.height() * static_cast<qint64>(2));
		uint pixel_count = tmpImg.width();
		uchar *ptr;
		uchar *ptr2;
		uint count, len;
		uchar c;
		for (int hh = 0; hh < tmpImg.height(); hh++)
		{
			count = 0;
			ptr = tmpImg.scanLine(hh);
			ptr2 = ptr + (intptr_t) tmpImg.width() * tmpImg.channels();
			ptr += component;
			while (count < pixel_count)
			{
				if (s.atEnd())
					return false;
				s >> c;
				len = c;
				if (len < 128)
				{
					// Copy next len+1 bytes literally.
					len++;
					count += len;
					while (len != 0)
					{
						s >> cbyte;
						if (ptr < ptr2)
						{
							if ((header.color_mode == CM_CMYK) && (component < 4))
								cbyte = 255 - cbyte;
							if ((header.color_mode == CM_GRAYSCALE) && (component != 3))
							{
								ptr -= component;
								ptr[0] = cbyte;
								ptr[1] = cbyte;
								ptr[2] = cbyte;
								ptr += component;
							}
							else if ((header.color_mode == CM_DUOTONE) && (component != 3))
							{
								ptr -= component;
								putDuotone(ptr, cbyte);
								ptr += component;
							}
							else if ((header.color_mode == CM_INDEXED) && (component != 3))
							{
								ptr -= component;
								int ccol = m_colorTable[cbyte];
								ptr[0] = qRed(ccol);
								ptr[1] = qGreen(ccol);
								ptr[2] = qBlue(ccol);
								ptr += component;
							}
							else
							{
								*ptr = cbyte;
							}
						}
						ptr += tmpImg.channels();
						len--;
					}
				}
				else if (len > 128)
				{
					// Next -len+1 bytes in the dest are replicated from next source byte.
					// (Interpret len as a negative 8-bit int.)
					len ^= 0xFF;
					len += 2;
					count += len;
					uchar val;
					s >> val;
					if ((header.color_mode == CM_CMYK) && (component < 4))
						val = 255 - val;
					while (len != 0)
					{
						if (ptr < ptr2)
						{
							if ((header.color_mode == CM_GRAYSCALE) && (component != 3))
							{
								ptr -= component;
								ptr[0] = val;
								ptr[1] = val;
								ptr[2] = val;
								ptr += component;
							}
							else if ((header.color_mode == CM_DUOTONE) && (component != 3))
							{
								ptr -= component;
								putDuotone(ptr, val);
								ptr += component;
							}
							else if ((header.color_mode == CM_INDEXED) && (component != 3))
							{
								ptr -= component;
								int ccol = m_colorTable[val];
								ptr[0] = qRed(ccol);
								ptr[1] = qGreen(ccol);
								ptr[2] = qBlue(ccol);
								ptr += component;
							}
							else
								*ptr = val;
						}
						ptr += tmpImg.channels();
						len--;
					}
				}
				else if (len == 128)
				{
					// No-op.
				}
			}
		}
	}
	s.device()->seek(base + layerInfo[layer].channelLen[channel]);
	return true;
}

bool ScImgDataLoader_PSD::loadLayerChannels(QDataStream& s, const PSDHeader& header, QList<PSDLayer>& layerInfo, uint layer, bool* firstLayer)
{
	// Find out if the data is compressed.
	// Known values:
	//   0: no compression
	//   1: RLE compressed
	qint64 base = s.device()->pos();
	qint64 base2 = base;
	uint channel_num = layerInfo[layer].channelLen.count();
	bool hasMask = false;
	bool hasAlpha = false;
	RawImage r2_image;
	RawImage mask;
	bool createOk = false;
	ScColorMgmtEngine engine(ScCore->defaultEngine);
	if (header.color_mode == CM_CMYK)
	{
		createOk = r2_image.create(layerInfo[layer].width, layerInfo[layer].height, qMax(channel_num, (uint)5));
		r2_image.fill(0);
	}
	else
	{
		createOk = r2_image.create(layerInfo[layer].width, layerInfo[layer].height, qMax(channel_num, (uint)4));
		r2_image.fill(0);
	}
	if (!createOk)
	{
		for (uint channel = 0; channel < channel_num; channel++)
		{
			base2 += layerInfo[layer].channelLen[channel];
		}
		s.device()->seek(base2);
		return false;
	}
	channel_num = qMin(channel_num, (uint) 39);
	uint components[40];
	for (uint channel = 0; channel < channel_num; channel++)
	{
		switch (layerInfo[layer].channelType[channel])
		{
		case 0:
			components[channel] = 0;
			break;
		case 1:
			components[channel] = 1;
			break;
		case 2:
			components[channel] = 2;
			break;
		case 3:
			components[channel] = 3;
			break;
		case -1:
			if (header.color_mode == CM_CMYK)
			{
				if (channel_num == 6)
					components[channel] = channel_num-2;
				else
					components[channel] = channel_num-1;
			}
			else
			{
				if (channel_num == 5)
					components[channel] = channel_num-2;
				else
				{
					if (header.color_mode == CM_GRAYSCALE)
						components[channel] = 3;
					else
						components[channel] = channel_num-1;
				}
			}
			hasAlpha = true;
			break;
		case -2:
			components[channel] = channel_num-1;
			break;
		}
	}
	if (!hasAlpha)
		r2_image.fill('\xff');
	for (uint channel = 0; channel < channel_num; channel++)
	{
		if (layerInfo[layer].channelType[channel] == -2)
		{
			if (!mask.create( layerInfo[layer].maskWidth, layerInfo[layer].maskHeight, 1 ))
				break;
			mask.fill(0);
			if (!loadChannel(s, header, layerInfo, layer, channel, 0, mask))
				break;
			hasMask = true;
		}
		if (!loadChannel(s, header, layerInfo, layer, channel, components[channel], r2_image))
			break;
	}
	for (uint channel = 0; channel < channel_num; channel++)
	{
		base2 += layerInfo[layer].channelLen[channel];
	}
	if (header.color_mode == CM_LABCOLOR)
	{
		ScColorProfile hsRGB = engine.createProfile_sRGB();
		ScColorProfile hLab  = engine.createProfile_Lab();
		ScColorTransform xform = engine.createTransform(hLab, Format_LabA_8, hsRGB, Format_RGBA_8, Intent_Perceptual, 0);
		for (int i = 0; i < r2_image.height(); i++)
		{
			uchar* ptr = r2_image.scanLine(i);
			xform.apply(ptr, ptr, r2_image.width());
		}
	}
	s.device()->seek(base2);
	QImage tmpImg2;
	if (header.color_mode == CM_CMYK)
		tmpImg2 = r2_image.convertToQImage(true);
	else
		tmpImg2 = r2_image.convertToQImage(false);
	QImage imt;
	double sx = tmpImg2.width() / 40.0;
	double sy = tmpImg2.height() / 40.0;
	imt = sy < sx ?  tmpImg2.scaled(qRound(tmpImg2.width() / sx), qRound(tmpImg2.height() / sx), Qt::IgnoreAspectRatio, Qt::SmoothTransformation) :
	      tmpImg2.scaled(qRound(tmpImg2.width() / sy), qRound(tmpImg2.height() / sy), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
	layerInfo[layer].thumb = imt.copy();
	if (hasMask)
	{
		QImage imt2;
		QImage tmpImg;
		tmpImg = mask.convertToQImage(true);
		double sx = tmpImg.width() / 40.0;
		double sy = tmpImg.height() / 40.0;
		imt2 = sy < sx ?  tmpImg.scaled(qRound(tmpImg.width() / sx), qRound(tmpImg.height() / sx), Qt::IgnoreAspectRatio, Qt::SmoothTransformation) :
	      tmpImg.scaled(qRound(tmpImg.width() / sy), qRound(tmpImg.height() / sy), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
		imt2.invertPixels();
		layerInfo[layer].thumb_mask = imt2.copy();
	}
	else
		layerInfo[layer].thumb_mask = QImage();
	if (!m_imageInfoRecord.isRequest || !m_imageInfoRecord.RequestProps.contains(layer))
		m_imageInfoRecord.RequestProps[layer].useMask = true;
	bool visible = !(layerInfo[layer].flags & 2);
	if ((m_imageInfoRecord.isRequest) && (m_imageInfoRecord.RequestProps.contains(layer)))
		visible = m_imageInfoRecord.RequestProps[layer].visible;
	if (visible)
	{
		unsigned int startSrcY, startSrcX, startDstY, startDstX;
		if (layerInfo[layer].ypos < 0)
		{
			startSrcY = abs(layerInfo[layer].ypos);
			startDstY = 0;
		}
		else
		{
			startSrcY = 0;
			startDstY = layerInfo[layer].ypos;
		}
		if (layerInfo[layer].xpos < 0)
		{
			startSrcX = abs(layerInfo[layer].xpos);
			startDstX = 0;
		}
		else
		{
			startSrcX = 0;
			startDstX = layerInfo[layer].xpos;
		}
		unsigned int startSrcXm; //, startSrcYm, startDstYm, startDstXm;
	/*	if (layerInfo[layer].maskYpos < 0)
		{
			startSrcYm = abs(layerInfo[layer].maskYpos);
			startDstYm = 0;
		}
		else
		{
			startSrcYm = 0;
			startDstYm = layerInfo[layer].maskYpos;
		}*/
		if (layerInfo[layer].maskXpos < 0)
		{
			startSrcXm = abs(layerInfo[layer].maskXpos);
	//		startDstXm = 0;
		}
		else
		{
			startSrcXm = 0;
	//		startDstXm = layerInfo[layer].maskXpos;
		}
		QString layBlend2 = layerInfo[layer].blend;
		if ((m_imageInfoRecord.isRequest) && (m_imageInfoRecord.RequestProps.contains(layer)))
			layBlend2 = m_imageInfoRecord.RequestProps[layer].blend;
		if (layBlend2 == QLatin1String("diss"))
		{
			hasAlpha = true;
			int layOpa = layerInfo[layer].opacity;
			if ((m_imageInfoRecord.isRequest) && (m_imageInfoRecord.RequestProps.contains(layer)))
				layOpa = m_imageInfoRecord.RequestProps[layer].opacity;
			for (int l = 0; l < r2_image.height(); l++)
			{
				srand(m_random_table[ l  % 4096]);
				for (int k = 0; k < r2_image.width(); k++)
				{
					int rand_val = rand() & 0xff;
					if (rand_val > layOpa)
						r2_image.setAlpha(k, l, 0);
				}
			}
		}
		if (*firstLayer)
		{
			unsigned char *s;
			unsigned char *d;
			for (int yi = static_cast<int>(startSrcY); yi < qMin(r2_image.height(),  r_image.height()); ++yi)
			{
				s = r2_image.scanLine( yi );
				d = r_image.scanLine( qMin(static_cast<int>(startDstY),  r_image.height()-1) );
				d += (intptr_t) qMin(static_cast<int>(startDstX), r_image.width() - 1) * r_image.channels();
				s += (intptr_t) qMin(static_cast<int>(startSrcX), r2_image.width() - 1) * r2_image.channels();
				for (int xi = static_cast<int>(startSrcX); xi < qMin(r2_image.width(),  r_image.width()); ++xi)
				{
					d[0] = s[0];
					d[1] = s[1];
					d[2] = s[2];
					if (header.color_mode != CM_CMYK)
					{
						if (hasAlpha)
							d[3] = s[3];
						else
							d[3] = 255;
					}
					else
					{
						d[3] = s[3];
						if (hasAlpha)
							d[4] = s[4];
						else
							d[4] = 255;
					}
					s += r2_image.channels();
					d += r_image.channels();
				}
				startDstY++;
			}
		}
		else
		{
			unsigned char *s;
			unsigned char *d;
			unsigned char *sm = nullptr;
			unsigned char r, g, b, src_r, src_g, src_b, src_a, src_alpha, dst_alpha;
			unsigned char a = 0;
			uchar new_r, new_g, new_b;
			unsigned int maxDestX;
			for (int i = static_cast<int>(startSrcY); i < layerInfo[layer].height; i++)
			{
				d = r_image.scanLine(qMin(static_cast<int>(startDstY),  r_image.height()-1));
				s = r2_image.scanLine(qMin(i, r2_image.height()-1));
				d += (intptr_t) qMin(static_cast<int>(startDstX), r_image.width() - 1) * r_image.channels();
				s += (intptr_t) qMin(static_cast<int>(startSrcX), r2_image.width() - 1) * r2_image.channels();
				sm = nullptr;
				if (hasMask)
				{
					sm = mask.scanLine(qMin(i, mask.height()-1));
					sm += (intptr_t) qMin(static_cast<int>(startSrcXm), mask.width() - 1) * mask.channels();
				}
				startDstY++;
				maxDestX = r_image.width() - startDstX + startSrcX - 1;
				for (unsigned int j = startSrcX; j < qMin(maxDestX, static_cast<unsigned int>(layerInfo[layer].width)); j++)
				{
					src_r = s[0];
					src_g = s[1];
					src_b = s[2];
					src_a = s[3];
					if (hasAlpha)
					{
						if (hasMask)
						{
							if (m_imageInfoRecord.RequestProps[layer].useMask)
								src_alpha = sm[0];
							else
								src_alpha = s[channel_num - 2];
						}
						else
						{
							if (header.color_mode == CM_GRAYSCALE)
								src_alpha = s[3];
							else
								src_alpha = s[channel_num - 1];
						}
					}
					else
						src_alpha = 255;
					if ((hasMask) && (m_imageInfoRecord.RequestProps[layer].useMask))
						src_alpha = sm[0];
					int layOpa = layerInfo[layer].opacity;
					if ((m_imageInfoRecord.isRequest) && (m_imageInfoRecord.RequestProps.contains(layer)))
						layOpa = m_imageInfoRecord.RequestProps[layer].opacity;
					QString layBlend = layerInfo[layer].blend;
					if ((m_imageInfoRecord.isRequest) && (m_imageInfoRecord.RequestProps.contains(layer)))
						layBlend = m_imageInfoRecord.RequestProps[layer].blend;
					if (layBlend != QLatin1String("diss"))
						src_alpha = INT_MULT(src_alpha, layOpa);
					if (header.color_mode == CM_CMYK)
						dst_alpha = d[4];
					else
						dst_alpha = d[3];
					if ((dst_alpha > 0) && (src_alpha > 0))
					{
						if (layBlend ==  QLatin1String("mul "))
						{
							src_r = INT_MULT(src_r, d[0]);
							src_g = INT_MULT(src_g, d[1]);
							src_b = INT_MULT(src_b, d[2]);
							if (header.color_mode == CM_CMYK)
								src_a = INT_MULT(src_a, d[3]);
						}
						else if (layBlend ==  QLatin1String("scrn"))
						{
							src_r = 255 - ((255-src_r) * (255-d[0]) / 128);
							src_g = 255 - ((255-src_g) * (255-d[1]) / 128);
							src_b = 255 - ((255-src_b) * (255-d[2]) / 128);
							if (header.color_mode == CM_CMYK)
								src_a = 255 - ((255-src_a) * (255-d[3]) / 128);
						}
						else if (layBlend ==  QLatin1String("over"))
						{
							src_g = d[1] < 128 ? src_g * d[1] / 128 : 255 - ((255-src_g) * (255-d[1]) / 128);
							src_b = d[2] < 128 ? src_b * d[2] / 128 : 255 - ((255-src_b) * (255-d[2]) / 128);
							src_a = d[3] < 128 ? src_a * d[3] / 128 : 255 - ((255-src_a) * (255-d[3]) / 128);
							if (header.color_mode == CM_CMYK)
								src_r = d[0] < 128 ? src_r * d[0] / 128 : 255 - ((255-src_r) * (255-d[0]) / 128);
						}
						else if (layBlend ==  QLatin1String("diff"))
						{
							src_r = d[0] > src_r ? d[0] - src_r : src_r - d[0];
							src_g = d[1] > src_g ? d[1] - src_g : src_g - d[1];
							src_b = d[2] > src_b ? d[2] - src_b : src_b - d[2];
							if (header.color_mode == CM_CMYK)
								src_a = d[3] > src_a ? d[3] - src_a : src_a - d[3];
						}
						else if (layBlend ==  QLatin1String("dark"))
						{
							src_r = d[0]  < src_r ? d[0]  : src_r;
							src_g = d[1] < src_g ? d[1] : src_g;
							src_b = d[2] < src_b ? d[2] : src_b;
							if (header.color_mode == CM_CMYK)
								src_a = d[3] < src_a ? d[3] : src_a;
						}
						else if (layBlend ==  QLatin1String("hLit"))
						{
							src_r = src_r < 128 ? src_r * d[0] / 128 : 255 - ((255-src_r) * (255-d[0]) / 128);
							src_g = src_g < 128 ? src_g * d[1] / 128 : 255 - ((255-src_g) * (255-d[1]) / 128);
							src_b = src_b < 128 ? src_b * d[2] / 128 : 255 - ((255-src_b) * (255-d[2]) / 128);
							if (header.color_mode == CM_CMYK)
								src_a = src_a < 128 ? src_a * d[3] / 128 : 255 - ((255-src_a) * (255-d[3]) / 128);
						}
						else if (layBlend ==  QLatin1String("sLit"))
						{
							src_r = src_r * d[0] / 256 + src_r * (255 - ((255-src_r)*(255-d[0]) / 256) - src_r * d[0] / 256) / 256;
							src_g = src_g * d[1] / 256 + src_g * (255 - ((255-src_g)*(255-d[1]) / 256) - src_g * d[1] / 256) / 256;
							src_b = src_b * d[2] / 256 + src_b * (255 - ((255-src_b)*(255-d[2]) / 256) - src_b * d[2] / 256) / 256;
							if (header.color_mode == CM_CMYK)
								src_a = src_a * d[3] / 256 + src_a * (255 - ((255-src_a)*(255-d[3]) / 256) - src_a * d[3] / 256) / 256;
						}
						else if (layBlend ==  QLatin1String("lite"))
						{
							src_r = d[0] < src_r ? src_r : d[0];
							src_g = d[1] < src_g ? src_g : d[1];
							src_b = d[2] < src_b ? src_b : d[2];
							if (header.color_mode == CM_CMYK)
								src_a = d[3] < src_a ? src_a : d[3];
						}
						else if (layBlend ==  QLatin1String("smud"))
						{
							src_r = d[0] + src_r - src_r * d[0] / 128;
							src_g = d[1] + src_g - src_g * d[1] / 128;
							src_b = d[2] + src_b - src_b * d[2] / 128;
							if (header.color_mode == CM_CMYK)
								src_a = d[3] + src_a - src_a * d[3] / 128;
						}
						else if (layBlend ==  QLatin1String("div "))
						{
							src_r = src_r == 255 ? 255 : ((d[0] * 256) / (255-src_r)) > 255 ? 255 : (d[0] * 256) / (255-src_r);
							src_g = src_g == 255 ? 255 : ((d[1] * 256) / (255-src_g)) > 255 ? 255 : (d[1] * 256) / (255-src_g);
							src_b = src_b == 255 ? 255 : ((d[2] * 256) / (255-src_b)) > 255 ? 255 : (d[2] * 256) / (255-src_b);
							if (header.color_mode == CM_CMYK)
								src_a = src_a == 255 ? 255 : ((d[3] * 256) / (255-src_a)) > 255 ? 255 : (d[3] * 256) / (255-src_a);
						}
						else if (layBlend ==  QLatin1String("idiv"))
						{
							src_r = src_r == 0 ? 0 : (255 - (((255-d[0]) * 256) / src_r)) < 0 ? 0 : 255 - (((255-d[0]) * 256) / src_r);
							src_g = src_g == 0 ? 0 : (255 - (((255-d[1]) * 256) / src_g)) < 0 ? 0 : 255 - (((255-d[1]) * 256) / src_g);
							src_b = src_b == 0 ? 0 : (255 - (((255-d[2]) * 256) / src_b)) < 0 ? 0 : 255 - (((255-d[2]) * 256) / src_b);
							if (header.color_mode == CM_CMYK)
								src_a = src_a == 0 ? 0 : (255 - (((255-d[3]) * 256) / src_a)) < 0 ? 0 : 255 - (((255-d[3]) * 256) / src_a);
						}
						else if (layBlend ==  QLatin1String("hue "))
						{
							if (header.color_mode != CM_CMYK)
							{
								new_r = d[0];
								new_g = d[1];
								new_b = d[2];
								RGBTOHSV(src_r, src_g, src_b);
								RGBTOHSV(new_r, new_g, new_b);
								new_r = src_r;
								HSVTORGB(new_r, new_g, new_b);
								src_r = new_r;
								src_g = new_g;
								src_b = new_b;
							}
						}
						else if (layBlend ==  QLatin1String("sat "))
						{
							if (header.color_mode != CM_CMYK)
							{
								new_r = d[0];
								new_g = d[1];
								new_b = d[2];
								RGBTOHSV(src_r, src_g, src_b);
								RGBTOHSV(new_r, new_g, new_b);
								new_g = src_g;
								HSVTORGB(new_r, new_g, new_b);
								src_r = new_r;
								src_g = new_g;
								src_b = new_b;
							}
						}
						else if (layBlend ==  QLatin1String("lum "))
						{
							if (header.color_mode != CM_CMYK)
							{
								new_r = d[0];
								new_g = d[1];
								new_b = d[2];
								RGBTOHSV(src_r, src_g, src_b);
								RGBTOHSV(new_r, new_g, new_b);
								new_b = src_b;
								HSVTORGB(new_r, new_g, new_b);
								src_r = new_r;
								src_g = new_g;
								src_b = new_b;
							}
						}
						else if (layBlend ==  QLatin1String("colr"))
						{
							if (header.color_mode != CM_CMYK)
							{
								new_r = d[0];
								new_g = d[1];
								new_b = d[2];
								RGBTOHLS(src_r, src_g, src_b);
								RGBTOHLS(new_r, new_g, new_b);
								new_r = src_r;
								new_b = src_b;
								HLSTORGB(new_r, new_g, new_b);
								src_r = new_r;
								src_g = new_g;
								src_b = new_b;
							}
						}
					}
					if (dst_alpha == 0)
					{
						r = src_r;
						g = src_g;
						b = src_b;
						a = src_a;
					}
					else
					{
						if (src_alpha > 0)
						{
							r = (d[0] * (255 - src_alpha) + src_r * src_alpha) / 255;
							g = (d[1] * (255 - src_alpha) + src_g * src_alpha) / 255;
							b = (d[2] * (255 - src_alpha) + src_b * src_alpha) / 255;
							if (header.color_mode == CM_CMYK)
								a = (d[3] * (255 - src_alpha) + src_a * src_alpha) / 255;
							if (layBlend !=  QLatin1String("diss"))
								src_alpha = dst_alpha + INT_MULT(255 - dst_alpha, src_alpha);
						}
					}
					if (src_alpha > 0)
					{
						d[0] = r;
						d[1] = g;
						d[2] = b;
						if (header.color_mode == CM_CMYK)
						{
							d[3] = a;
							d[4] = src_alpha;
						}
						else
							d[3] = src_alpha;
					}
					s += r2_image.channels();
					d += r_image.channels();
					if (hasMask)
						sm += mask.channels();
				}
			}
		}
	}
	*firstLayer = false;
	return true;
}

bool ScImgDataLoader_PSD::loadLayer(QDataStream& s, const PSDHeader& header)
{
	ScColorMgmtEngine engine(ScCore->defaultEngine);
	// Find out if the data is compressed.
	// Known values:
	//   0: no compression
	//   1: RLE compressed
	ushort compression;
	uchar cbyte;
	s >> compression;
	if (compression > 1)
	{
		// Unknown compression type.
		return false;
	}
	uint channel_num = header.channel_count;
	r_image.fill('\xff');
	const uint pixel_count = header.height * header.width;
	static const uint components[5] = {0, 1, 2, 3, 4};
	if (compression)
	{
		// Skip row lengths.
		ushort w;
		for (uint i = 0; i < header.height * header.channel_count; i++)
		{
			s >> w;
		}
		// Read RLE data.
		uchar * ptr;
		uint count = 0;
		uchar c;
		uint len;
		for (uint channel = 0; channel < channel_num; channel++)
		{
			ptr = r_image.bits() + components[channel];
			count = 0;
			while (count < pixel_count)
			{
				if (s.atEnd())
					return false;
				s >> c;
				len = c;
				if (len < 128)
				{
					// Copy next len+1 bytes literally.
					len++;
					count += len;
					if (count > pixel_count)
						return false;
					while (len != 0)
					{
						s >> cbyte;
						if ((header.color_mode == CM_CMYK) && (components[channel] < 4))
							cbyte = 255 - cbyte;
						if ((header.color_mode == CM_GRAYSCALE) && (components[channel] != 3))
						{
							ptr -= components[channel];
							ptr[0] = cbyte;
							ptr[1] = cbyte;
							ptr[2] = cbyte;
							ptr += components[channel];
						}
						else if ((header.color_mode == CM_DUOTONE) && (components[channel] != 3))
						{
							ptr -= components[channel];
							putDuotone(ptr, cbyte);
							ptr += components[channel];
						}
						else if ((header.color_mode == CM_INDEXED) && (components[channel] != 3))
						{
							ptr -= components[channel];
							int ccol = m_colorTable[cbyte];
							ptr[0] = qRed(ccol);
							ptr[1] = qGreen(ccol);
							ptr[2] = qBlue(ccol);
							ptr += components[channel];
						}
						else
							*ptr = cbyte;
						ptr += r_image.channels();
						len--;
					}
				}
				else if (len > 128)
				{
					// Next -len+1 bytes in the dest are replicated from next source byte.
					// (Interpret len as a negative 8-bit int.)
					len ^= 0xFF;
					len += 2;
					count += len;
					if (s.atEnd() || count > pixel_count)
						return false;
					uchar val;
					s >> val;
					if ((header.color_mode == CM_CMYK) && (components[channel] < 4))
						val = 255 - val;
					while (len != 0)
					{
						if ((header.color_mode == CM_GRAYSCALE) && (components[channel] != 3))
						{
							ptr -= components[channel];
							ptr[0] = val;
							ptr[1] = val;
							ptr[2] = val;
							ptr += components[channel];
						}
						else if ((header.color_mode == CM_DUOTONE) && (components[channel] != 3))
						{
							ptr -= components[channel];
							putDuotone(ptr, val);
							ptr += components[channel];
						}
						else if ((header.color_mode == CM_INDEXED) && (components[channel] != 3))
						{
							ptr -= components[channel];
							int ccol = m_colorTable[val];
							ptr[0] = qRed(ccol);
							ptr[1] = qGreen(ccol);
							ptr[2] = qBlue(ccol);
							ptr += components[channel];
						}
						else
							*ptr = val;
						ptr += r_image.channels();
						len--;
					}
				}
				else if (len == 128)
				{
					// No-op.
				}
			}
		}
	}
	else
	{
		// We're at the raw image data.  It's each channel in order (Red, Green, Blue, Alpha, ...)
		// where each channel consists of an 8-bit value for each pixel in the image.
		// Read the data by channel.
		uchar * ptr;
		uint count = 0;
		for (uint channel = 0; channel < channel_num; channel++)
		{
			ptr =  r_image.bits() + components[channel];
			// Read the data.
			count = pixel_count;
			while (count != 0)
			{
				s >> cbyte;
				if ((header.color_mode == CM_CMYK) && (components[channel] < 4))
					cbyte = 255 - cbyte;
				if ((header.color_mode == CM_GRAYSCALE) && (components[channel] != 3))
				{
					ptr -= components[channel];
					ptr[0] = cbyte;
					ptr[1] = cbyte;
					ptr[2] = cbyte;
					ptr += components[channel];
				}
				else if ((header.color_mode == CM_DUOTONE) && (components[channel] != 3))
				{
					ptr -= components[channel];
					putDuotone(ptr, cbyte);
					ptr += components[channel];
				}
				else if ((header.color_mode == CM_INDEXED) && (components[channel] != 3))
				{
					ptr -= components[channel];
					int ccol = m_colorTable[cbyte];
					ptr[0] = qRed(ccol);
					ptr[1] = qGreen(ccol);
					ptr[2] = qBlue(ccol);
					ptr += components[channel];
				}
				else
					*ptr = cbyte;
				ptr += r_image.channels();
				count--;
			}
		}
	}
	if (header.color_mode == CM_LABCOLOR)
	{
		ScColorProfile hsRGB = engine.createProfile_sRGB();
		ScColorProfile hLab  = engine.createProfile_Lab();
		ScColorTransform xform = engine.createTransform(hLab, Format_LabA_8, hsRGB, Format_RGBA_8, Intent_Perceptual, 0);
		for (int i = 0; i < r_image.height(); i++)
		{
			uchar* ptr = r_image.scanLine(i);
			xform.apply(ptr, ptr, r_image.width());
		}
	}
	return true;
}

QString ScImgDataLoader_PSD::getLayerString(QDataStream & s)
{
	uchar len, tmp;
	uint adj;
	QString ret;
	s >> len;
	if (len == 0)
	{
		s >> tmp;
		s >> tmp;
		s >> tmp;
		return ret;
	}
	for (int i = 0; i < len; i++)
	{
		s >> tmp;
		ret += QChar(tmp);
	}
	adj = 0;
	if (((ret.length()+1) % 4) != 0)
		adj = 4 - ((ret.length()+1) % 4);
	s.device()->seek( s.device()->pos() + adj );
	return ret;
}

bool ScImgDataLoader_PSD::IsValid( const PSDHeader & header ) const
{
	return header.signature == 0x38425053;
}

// Check that the header is supported.
bool ScImgDataLoader_PSD::IsSupported(const PSDHeader& header) const
{
	if (header.version != 1)
		return false;
	if (header.channel_count > 16)
		return false;
	if (header.depth != 8)
		return false;
	return (header.color_mode == CM_RGB) || (header.color_mode == CM_CMYK) || (header.color_mode == CM_LABCOLOR)
	 || (header.color_mode == CM_GRAYSCALE) || (header.color_mode == CM_INDEXED) || (header.color_mode == CM_DUOTONE);
}

void ScImgDataLoader_PSD::putDuotone(uchar *ptr, uchar cbyte)
{
	CMYKColor cmyk;
	int c, c1, c2, c3, m, m1, m2, m3, y, y1, y2, y3, k, k1, k2, k3;
	uchar cb = 255 - cbyte;
	ScColor col;
	if (m_colorTableSc.count() == 1)
	{
		m_colorTableSc[0].getRawRGBColor(&c, &m, &y);
		ptr[0] = qMin((c * m_curveTable1[(int)cbyte]) >> 8, 255);
		ptr[1] = qMin((m * m_curveTable1[(int)cbyte]) >> 8, 255);
		ptr[2] = qMin((y * m_curveTable1[(int)cbyte]) >> 8, 255);
	}
	else if (m_colorTableSc.count() == 2)
	{
		ScColorEngine::getCMYKValues(m_colorTableSc[0], nullptr, cmyk);
		cmyk.getValues(c, m, y, k);
		c = qMin((c * m_curveTable1[(int)cb]) >> 8, 255);
		m = qMin((m * m_curveTable1[(int)cb]) >> 8, 255);
		y = qMin((y * m_curveTable1[(int)cb]) >> 8, 255);
		k = qMin((k * m_curveTable1[(int)cb]) >> 8, 255);
		ScColorEngine::getCMYKValues(m_colorTableSc[1], nullptr, cmyk);
		cmyk.getValues(c1, m1, y1, k1);
		c1 = qMin((c1 * m_curveTable2[(int)cb]) >> 8, 255);
		m1 = qMin((m1 * m_curveTable2[(int)cb]) >> 8, 255);
		y1 = qMin((y1 * m_curveTable2[(int)cb]) >> 8, 255);
		k1 = qMin((k1 * m_curveTable2[(int)cb]) >> 8, 255);
		col = ScColor(qMin(c+c1, 255), qMin(m+m1, 255), qMin(y+y1, 255), qMin(k+k1, 255));
		col.getRawRGBColor(&c, &m, &y);
		ptr[0] = c;
		ptr[1] = m;
		ptr[2] = y;
	}
	else if (m_colorTableSc.count() == 3)
	{
		ScColorEngine::getCMYKValues(m_colorTableSc[0], nullptr, cmyk);
		cmyk.getValues(c, m, y, k);
		c = qMin((c * m_curveTable1[(int)cb]) >> 8, 255);
		m = qMin((m * m_curveTable1[(int)cb]) >> 8, 255);
		y = qMin((y * m_curveTable1[(int)cb]) >> 8, 255);
		k = qMin((k * m_curveTable1[(int)cb]) >> 8, 255);
		ScColorEngine::getCMYKValues(m_colorTableSc[1], nullptr, cmyk);
		cmyk.getValues(c1, m1, y1, k1);
		c1 = qMin((c1 * m_curveTable2[(int)cb]) >> 8, 255);
		m1 = qMin((m1 * m_curveTable2[(int)cb]) >> 8, 255);
		y1 = qMin((y1 * m_curveTable2[(int)cb]) >> 8, 255);
		k1 = qMin((k1 * m_curveTable2[(int)cb]) >> 8, 255);
		ScColorEngine::getCMYKValues(m_colorTableSc[2], nullptr, cmyk);
		cmyk.getValues(c2, m2, y2, k2);
		c2 = qMin((c2 * m_curveTable3[(int)cb]) >> 8, 255);
		m2 = qMin((m2 * m_curveTable3[(int)cb]) >> 8, 255);
		y2 = qMin((y2 * m_curveTable3[(int)cb]) >> 8, 255);
		k2 = qMin((k2 * m_curveTable3[(int)cb]) >> 8, 255);
		col = ScColor(qMin(c+c1+c2, 255), qMin(m+m1+m2, 255), qMin(y+y1+y2, 255), qMin(k+k1+k2, 255));
		col.getRawRGBColor(&c, &m, &y);
		ptr[0] = c;
		ptr[1] = m;
		ptr[2] = y;
	}
	else if (m_colorTableSc.count() == 4)
	{
		ScColorEngine::getCMYKValues(m_colorTableSc[0], nullptr, cmyk);
		cmyk.getValues(c, m, y, k);
		c = qMin((c * m_curveTable1[(int)cb]) >> 8, 255);
		m = qMin((m * m_curveTable1[(int)cb]) >> 8, 255);
		y = qMin((y * m_curveTable1[(int)cb]) >> 8, 255);
		k = qMin((k * m_curveTable1[(int)cb]) >> 8, 255);
		ScColorEngine::getCMYKValues(m_colorTableSc[1], nullptr, cmyk);
		cmyk.getValues(c1, m1, y1, k1);
		c1 = qMin((c1 * m_curveTable2[(int)cb]) >> 8, 255);
		m1 = qMin((m1 * m_curveTable2[(int)cb]) >> 8, 255);
		y1 = qMin((y1 * m_curveTable2[(int)cb]) >> 8, 255);
		k1 = qMin((k1 * m_curveTable2[(int)cb]) >> 8, 255);
		ScColorEngine::getCMYKValues(m_colorTableSc[2], nullptr, cmyk);
		cmyk.getValues(c2, m2, y2, k2);
		c2 = qMin((c2 * m_curveTable3[(int)cb]) >> 8, 255);
		m2 = qMin((m2 * m_curveTable3[(int)cb]) >> 8, 255);
		y2 = qMin((y2 * m_curveTable3[(int)cb]) >> 8, 255);
		k2 = qMin((k2 * m_curveTable3[(int)cb]) >> 8, 255);
		ScColorEngine::getCMYKValues(m_colorTableSc[3], nullptr, cmyk);
		cmyk.getValues(c3, m3, y3, k3);
		c3 = qMin((c3 * m_curveTable4[(int)cb]) >> 8, 255);
		m3 = qMin((m3 * m_curveTable4[(int)cb]) >> 8, 255);
		y3 = qMin((y3 * m_curveTable4[(int)cb]) >> 8, 255);
		k3 = qMin((k3 * m_curveTable4[(int)cb]) >> 8, 255);
		col = ScColor(qMin(c+c1+c2+c3, 255), qMin(m+m1+m2+m3, 255), qMin(y+y1+y2+y3, 255), qMin(k+k1+k2+k3, 255));
		col.getRawRGBColor(&c, &m, &y);
		ptr[0] = c;
		ptr[1] = m;
		ptr[2] = y;
	}
}
