/*
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 "scimage.h"
#include "scribus.h"
#include "scribusapp.h"
#include <qtextstream.h>
#include <cassert>
#ifdef HAVE_CMS
	#include CMS_INC
	#include "cmsutil.h"
extern cmsHPROFILE CMSoutputProf;
extern cmsHPROFILE CMSprinterProf;
extern cmsHTRANSFORM stdTransCMYK2MonG;
extern cmsHTRANSFORM stdTransRGBDoc2MonG;
extern cmsHTRANSFORM stdProofRGBG;
extern cmsHTRANSFORM stdTransImgG;
extern cmsHTRANSFORM stdProofImgG;
extern bool BlackPoint;
extern bool SoftProofing;
extern bool Gamut;
extern bool CMSuse;
extern int IntentMonitor;
extern int IntentPrinter;
#endif
#include "gsutil.h"
#include "exif.h"
#include "commonstrings.h"


typedef struct my_error_mgr
{
	struct jpeg_error_mgr pub;            /* "public" fields */
	jmp_buf setjmp_buffer;  /* for return to caller */
}
*my_error_ptr;

static void my_error_exit (j_common_ptr cinfo)
{
	my_error_ptr myerr = (my_error_ptr) cinfo->err;
	(*cinfo->err->output_message) (cinfo);
	longjmp (myerr->setjmp_buffer, 1);
}

static QDataStream & operator>> ( QDataStream & s, ScImage::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;
}

ScImage::ScImage(const QImage & image) : QImage(image)
{
	initialize();
}

ScImage::ScImage() : QImage()
{
	initialize();
}

ScImage::ScImage( int width, int height ) : QImage( width, height, 32 )
{
	initialize();
}

void ScImage::initialize()
{
	imgInfo.xres = 72;
	imgInfo.yres = 72;
	imgInfo.colorspace = 0;
	imgInfo.valid = false;
	imgInfo.isRequest = false;
	imgInfo.progressive = false;
	imgInfo.isEmbedded = false;
	imgInfo.exifDataValid = false;
	imgInfo.lowResType = 1;
	imgInfo.lowResScale = 1.0;
	imgInfo.PDSpathData.clear();
	imgInfo.RequestProps.clear();
	imgInfo.clipPath = "";
	imgInfo.usedPath = "";
	imgInfo.layerInfo.clear();
	imgInfo.exifInfo.cameraName = "";
	imgInfo.exifInfo.cameraVendor = "";
	imgInfo.exifInfo.thumbnail = QImage();
	imgInfo.BBoxX = 0;
	imgInfo.BBoxH = 0;
}

ScImage::~ScImage()
{
	curveTable.resize(0);
}

void ScImage::applyEffect(QValueList<imageEffect> effectsList, QMap<QString,ScColor> colors, bool cmyk)
{
	if (effectsList.count() != 0)
	{
		for (uint a = 0; a < effectsList.count(); ++a)
		{
			if ((*effectsList.at(a)).effectCode == EF_INVERT)
				invert(cmyk);
			if ((*effectsList.at(a)).effectCode == EF_GRAYSCALE)
				toGrayscale(cmyk);
			if ((*effectsList.at(a)).effectCode == EF_COLORIZE)
			{
				QString tmpstr = (*effectsList.at(a)).effectParameters;
				QString col = CommonStrings::None;
				int shading = 100;
				QTextStream fp(&tmpstr, IO_ReadOnly);
				fp >> col;
				fp >> shading;
				colorize(colors[col], shading, cmyk);
			}
			if ((*effectsList.at(a)).effectCode == EF_BRIGHTNESS)
			{
				QString tmpstr = (*effectsList.at(a)).effectParameters;
				int brightnessValue = 0;
				QTextStream fp(&tmpstr, IO_ReadOnly);
				fp >> brightnessValue;
				brightness(brightnessValue, cmyk);
			}
			if ((*effectsList.at(a)).effectCode == EF_CONTRAST)
			{
				QString tmpstr = (*effectsList.at(a)).effectParameters;
				int contrastValue = 0;
				QTextStream fp(&tmpstr, IO_ReadOnly);
				fp >> contrastValue;
				contrast(contrastValue, cmyk);
			}
			if ((*effectsList.at(a)).effectCode == EF_SHARPEN)
			{
				QString tmpstr = (*effectsList.at(a)).effectParameters;
				double radius, sigma;
				QTextStream fp(&tmpstr, IO_ReadOnly);
				fp >> radius;
				fp >> sigma;
				sharpen(radius, sigma);
			}
			if ((*effectsList.at(a)).effectCode == EF_BLUR)
			{
				QString tmpstr = (*effectsList.at(a)).effectParameters;
				double radius, sigma;
				QTextStream fp(&tmpstr, IO_ReadOnly);
				fp >> radius;
				fp >> sigma;
				blur(radius, sigma);
			}
			if ((*effectsList.at(a)).effectCode == EF_SOLARIZE)
			{
				QString tmpstr = (*effectsList.at(a)).effectParameters;
				double sigma;
				QTextStream fp(&tmpstr, IO_ReadOnly);
				fp >> sigma;
				solarize(sigma, cmyk);
			}
		}
	}
}
/*
void ScImage::liberateMemory(void **memory)
{
	assert(memory != (void **)NULL);
	if(*memory == (void *)NULL)
		return;
	free(*memory);
	*memory=(void *) NULL;
}
*/
void ScImage::solarize(double factor, bool cmyk)
{
	curveTable.resize(256);
	int fk = qRound(255 / factor);
	for (int i = 0; i < 256; ++i)
	{
		curveTable[i] = QMIN(255, static_cast<int>(i / fk) * fk);
	}
	applyCurve(cmyk);
}

void ScImage::blurScanLine(double *kernel, int width, unsigned int *src, unsigned int *dest, int columns)
{
	register double *p;
	unsigned int *q;
	register int x;
	register long i;
	double red, green, blue, alpha;
	double scale = 0.0;
	if(width > columns)
	{
		for(x=0; x < columns; ++x)
		{
			scale = 0.0;
			red = blue = green = alpha = 0.0;
			p = kernel;
			q = src;
			for(i=0; i < columns; ++i)
			{
				if((i >= (x-width/2)) && (i <= (x+width/2)))
				{
					red += (*p)*(qRed(*q)*257);
					green += (*p)*(qGreen(*q)*257);
					blue += (*p)*(qBlue(*q)*257);
					alpha += (*p)*(qAlpha(*q)*257);
				}
				if(((i+width/2-x) >= 0) && ((i+width/2-x) < width))
					scale+=kernel[i+width/2-x];
				p++;
				q++;
			}
			scale = 1.0/scale;
			red = scale*(red+0.5);
			green = scale*(green+0.5);
			blue = scale*(blue+0.5);
			alpha = scale*(alpha+0.5);
			red = red < 0 ? 0 : red > 65535 ? 65535 : red;
			green = green < 0 ? 0 : green > 65535 ? 65535 : green;
			blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
			alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
			dest[x] = qRgba((unsigned char)(red/257UL),
			                (unsigned char)(green/257UL),
			                (unsigned char)(blue/257UL),
			                (unsigned char)(alpha/257UL));
		}
		return;
	}

	for(x=0; x < width/2; ++x)
	{
		scale = 0.0;
		red = blue = green = alpha = 0.0;
		p = kernel+width/2-x;
		q = src;
		for(i=width/2-x; i < width; ++i)
		{
			red += (*p)*(qRed(*q)*257);
			green += (*p)*(qGreen(*q)*257);
			blue += (*p)*(qBlue(*q)*257);
			alpha += (*p)*(qAlpha(*q)*257);
			scale += (*p);
			p++;
			q++;
		}
		scale=1.0/scale;
		red = scale*(red+0.5);
		green = scale*(green+0.5);
		blue = scale*(blue+0.5);
		alpha = scale*(alpha+0.5);
		red = red < 0 ? 0 : red > 65535 ? 65535 : red;
		green = green < 0 ? 0 : green > 65535 ? 65535 : green;
		blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
		alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
		dest[x] = qRgba((unsigned char)(red/257UL),
		                (unsigned char)(green/257UL),
		                (unsigned char)(blue/257UL),
		                (unsigned char)(alpha/257UL));
	}
	for(; x < columns-width/2; ++x)
	{
		red = blue = green = alpha = 0.0;
		p = kernel;
		q = src+(x-width/2);
		for (i=0; i < (long) width; ++i)
		{
			red += (*p)*(qRed(*q)*257);
			green += (*p)*(qGreen(*q)*257);
			blue += (*p)*(qBlue(*q)*257);
			alpha += (*p)*(qAlpha(*q)*257);
			p++;
			q++;
		}
		red = scale*(red+0.5);
		green = scale*(green+0.5);
		blue = scale*(blue+0.5);
		alpha = scale*(alpha+0.5);
		red = red < 0 ? 0 : red > 65535 ? 65535 : red;
		green = green < 0 ? 0 : green > 65535 ? 65535 : green;
		blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
		alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
		dest[x] = qRgba((unsigned char)(red/257UL),
		                (unsigned char)(green/257UL),
		                (unsigned char)(blue/257UL),
		                (unsigned char)(alpha/257UL));
	}
	for(; x < columns; ++x)
	{
		red = blue = green = alpha = 0.0;
		scale=0;
		p = kernel;
		q = src+(x-width/2);
		for(i=0; i < columns-x+width/2; ++i)
		{
			red += (*p)*(qRed(*q)*257);
			green += (*p)*(qGreen(*q)*257);
			blue += (*p)*(qBlue(*q)*257);
			alpha += (*p)*(qAlpha(*q)*257);
			scale += (*p);
			p++;
			q++;
		}
		scale=1.0/scale;
		red = scale*(red+0.5);
		green = scale*(green+0.5);
		blue = scale*(blue+0.5);
		alpha = scale*(alpha+0.5);
		red = red < 0 ? 0 : red > 65535 ? 65535 : red;
		green = green < 0 ? 0 : green > 65535 ? 65535 : green;
		blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
		alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
		dest[x] = qRgba((unsigned char)(red/257UL),
		                (unsigned char)(green/257UL),
		                (unsigned char)(blue/257UL),
		                (unsigned char)(alpha/257UL));
	}
}

int ScImage::getBlurKernel(int width, double sigma, double **kernel)
{
	double alpha, normalize;
	register long i;
	int bias;
	assert(sigma != 0.0);
	if(width == 0)
		width = 3;
	*kernel=(double *)malloc(width*sizeof(double));
	if(*kernel == (double *)NULL)
		return(0);
	memset(*kernel, 0, width*sizeof(double));
	bias = 3*width/2;
	for(i=(-bias); i <= bias; i++)
	{
		alpha=exp(-((double) i*i)/(2.0*3*3*sigma*sigma));
		(*kernel)[(i+bias)/3]+=alpha/(2.50662827463100024161235523934010416269302368164062*sigma);
	}
	normalize=0;
	for(i=0; i < width; i++)
		normalize+=(*kernel)[i];
	for(i=0; i < width; i++)
		(*kernel)[i]/=normalize;
	return(width);
}

void ScImage::blur(double radius, double sigma)
{
	double *kernel;
	QImage dest;
	int widthk;
	int x, y;
	unsigned int *scanline, *temp;
	unsigned int *p, *q;
	if(sigma == 0.0)
		return;
	kernel=(double *) NULL;
	if(radius > 0)
		widthk=getBlurKernel((int) (2*ceil(radius)+1),sigma,&kernel);
	else
	{
		double *last_kernel;
		last_kernel=(double *) NULL;
		widthk=getBlurKernel(3,sigma,&kernel);
		while ((long) (255*kernel[0]) > 0)
		{
			if(last_kernel != (double *)NULL)
			{
				free(last_kernel);
//				liberateMemory((void **) &last_kernel);
			}
			last_kernel=kernel;
			kernel = (double *)NULL;
			widthk = getBlurKernel(widthk+2, sigma, &kernel);
		}
		if(last_kernel != (double *) NULL)
		{
			free(kernel);
//			liberateMemory((void **) &kernel);
			widthk-=2;
			kernel = last_kernel;
		}
	}
	if(widthk < 3)
	{
		free(kernel);
//		liberateMemory((void **) &kernel);
		return;
	}
	dest.create(width(), height(), 32);
	scanline = (unsigned int *)malloc(sizeof(unsigned int)*height());
	temp = (unsigned int *)malloc(sizeof(unsigned int)*height());
	for(y=0; y < height(); ++y)
	{
		p = (unsigned int *)scanLine(y);
		q = (unsigned int *)dest.scanLine(y);
		blurScanLine(kernel, widthk, p, q, width());
	}
	unsigned int **srcTable = (unsigned int **)jumpTable();
	unsigned int **destTable = (unsigned int **)dest.jumpTable();
	for(x=0; x < width(); ++x)
	{
		for(y=0; y < height(); ++y)
		{
			scanline[y] = srcTable[y][x];
		}
		blurScanLine(kernel, widthk, scanline, temp, height());
		for(y=0; y < height(); ++y)
		{
			destTable[y][x] = temp[y];
		}
	}
	free(scanline);
	free(temp);
	free(kernel);
//	liberateMemory((void **) &scanline);
//	liberateMemory((void **) &temp);
//	liberateMemory((void **) &kernel);
	for( int yi=0; yi < dest.height(); ++yi )
	{
		QRgb *s = (QRgb*)(dest.scanLine( yi ));
		QRgb *d = (QRgb*)(scanLine( yi ));
		for(int xi=0; xi < dest.width(); ++xi )
		{
			(*d) = (*s);
			s++;
			d++;
		}
	}
	return;
}

bool ScImage::convolveImage(QImage *dest, const unsigned int order, const double *kernel)
{
	long widthk;
	double red, green, blue, alpha;
	double normalize, *normal_kernel;
	register const double *k;
	register unsigned int *q;
	int x, y, mx, my, sx, sy;
	long i;
	int mcx, mcy;
	widthk = order;
	if((widthk % 2) == 0)
		return(false);
	normal_kernel = (double *)malloc(widthk*widthk*sizeof(double));
	if(!normal_kernel)
		return(false);
	dest->reset();
	dest->create(width(), height(), 32);
	normalize=0.0;
	for(i=0; i < (widthk*widthk); i++)
		normalize += kernel[i];
	if(fabs(normalize) <= 1.0e-12)
		normalize=1.0;
	normalize=1.0/normalize;
	for(i=0; i < (widthk*widthk); i++)
		normal_kernel[i] = normalize*kernel[i];
	unsigned int **jumpTablek = (unsigned int **)jumpTable();
	for(y=0; y < dest->height(); ++y)
	{
		sy = y-(widthk/2);
		q = (unsigned int *)dest->scanLine(y);
		for(x=0; x < dest->width(); ++x)
		{
			k = normal_kernel;
			red = green = blue = alpha = 0;
			sy = y-(widthk/2);
			for(mcy=0; mcy < widthk; ++mcy, ++sy)
			{
				my = sy < 0 ? 0 : sy > height()-1 ? height()-1 : sy;
				sx = x+(-widthk/2);
				for(mcx=0; mcx < widthk; ++mcx, ++sx)
				{
					mx = sx < 0 ? 0 : sx > width()-1 ? width()-1 : sx;
					red += (*k)*(qRed(jumpTablek[my][mx])*257);
					green += (*k)*(qGreen(jumpTablek[my][mx])*257);
					blue += (*k)*(qBlue(jumpTablek[my][mx])*257);
					alpha += (*k)*(qAlpha(jumpTablek[my][mx])*257);
					++k;
				}
			}
			red = red < 0 ? 0 : red > 65535 ? 65535 : red+0.5;
			green = green < 0 ? 0 : green > 65535 ? 65535 : green+0.5;
			blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue+0.5;
			alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha+0.5;
			*q++ = qRgba((unsigned char)(red/257UL),
			             (unsigned char)(green/257UL),
			             (unsigned char)(blue/257UL),
			             (unsigned char)(alpha/257UL));
		}
	}
	free(normal_kernel);
	return(true);
}

int ScImage::getOptimalKernelWidth(double radius, double sigma)
{
	double normalize, value;
	long width;
	register long u;
	assert(sigma != 0.0);
	if(radius > 0.0)
		return((int)(2.0*ceil(radius)+1.0));
	for(width=5; ;)
	{
		normalize=0.0;
		for(u=(-width/2); u <= (width/2); u++)
			normalize+=exp(-((double) u*u)/(2.0*sigma*sigma))/(2.50662827463100024161235523934010416269302368164062*sigma);
		u=width/2;
		value=exp(-((double) u*u)/(2.0*sigma*sigma))/(2.50662827463100024161235523934010416269302368164062*sigma)/normalize;
		if((long)(65535*value) <= 0)
			break;
		width+=2;
	}
	return((int)width-2);
}

void ScImage::sharpen(double radius, double sigma)
{
	double alpha, normalize, *kernel;
	int widthk;
	register long i, u, v;
	QImage dest;
	if(sigma == 0.0)
		return;
	widthk = getOptimalKernelWidth(radius, sigma);
	if(width() < widthk)
		return;
	kernel = (double *)malloc(widthk*widthk*sizeof(double));
	if(!kernel)
		return;
	i = 0;
	normalize=0.0;
	for (v=(-widthk/2); v <= (widthk/2); v++)
	{
		for (u=(-widthk/2); u <= (widthk/2); u++)
		{
			alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma));
			kernel[i]=alpha/(2.0*3.14159265358979323846264338327950288419716939937510*sigma*sigma);
			normalize+=kernel[i];
			i++;
		}
	}
	kernel[i/2]=(-2.0)*normalize;
	convolveImage(&dest, widthk, kernel);
	free(kernel);
//	liberateMemory((void **) &kernel);
	for( int yi=0; yi < dest.height(); ++yi )
	{
		QRgb *s = (QRgb*)(dest.scanLine( yi ));
		QRgb *d = (QRgb*)(scanLine( yi ));
		for(int xi=0; xi < dest.width(); ++xi )
		{
			(*d) = (*s);
			s++;
			d++;
		}
	}
	return;
}

void ScImage::contrast(int contrastValue, bool cmyk)
{
	curveTable.resize(256);
	QPoint p1(0,0 - contrastValue);
	QPoint p2(256, 256 + contrastValue);
	double mc = (p1.y() - p2.y()) / (double)(p1.x() - p2.x());
	for (int i = 0; i < 256; ++i)
	{
		curveTable[i] = QMIN(255, QMAX(0, int(i * mc) + p1.y()));
	}
	applyCurve(cmyk);
}

void ScImage::brightness(int brightnessValue, bool cmyk)
{
	curveTable.resize(256);
	QPoint p1(0,0 + brightnessValue);
	QPoint p2(256, 256 + brightnessValue);
	double mc = (p1.y() - p2.y()) / (double)(p1.x() - p2.x());
	for (int i = 0; i < 256; ++i)
	{
		curveTable[i] = QMIN(255, QMAX(0, int(i * mc) + p1.y()));
	}
	applyCurve(cmyk);
}

void ScImage::applyCurve(bool cmyk)
{
	int h = height();
	int w = width();
	for( int yi=0; yi < h; ++yi )
	{
		QRgb * s = (QRgb*)(scanLine( yi ));
		for( int xi=0; xi < w; ++xi )
		{
			QRgb r=*s;
			int c, m, y, k;
			if (cmyk)
			{
				unsigned char *p = (unsigned char *) s;
				unsigned char rc = 255 - QMIN(255, p[0] + p[3]);
				unsigned char gc = 255 - QMIN(255, p[1] + p[3]);
				unsigned char bc = 255 - QMIN(255, p[2] + p[3]);
				c = 255 - curveTable[(int)rc];
				m = 255 - curveTable[(int)gc];
				y = 255 - curveTable[(int)bc];
				k = QMIN(QMIN(c, m), y);
				*s = qRgba(y - k, m - k, c - k, k );
			}
			else
			{
				c = curveTable[qRed(r)];
				m = curveTable[qGreen(r)];
				y = curveTable[qBlue(r)];
				k = qAlpha(r);
				*s = qRgba(c, m, y, k);
			}
			s++;
		}
	}
}

void ScImage::colorize(ScColor color, int shade, bool cmyk)
{
	int h = height();
	int w = width();
	int cc, cm, cy, ck;
	int hu, sa, v;
	ScColor tmp2;
	QColor tmpR;
	if (cmyk)
		color.getShadeColorCMYK(&cc, &cm, &cy, &ck, shade);
	else
	{
		ck = 0;
		color.getShadeColorRGB(&cc, &cm, &cy, shade);
	}
	for( int yi=0; yi < h; ++yi )
	{
		QRgb * s = (QRgb*)(scanLine( yi ));
		for( int xi=0; xi < w; ++xi )
		{
			QRgb r=*s;
			if (cmyk)
			{
				double k = QMIN(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r) + qAlpha(r)), 255) / 255.0;
				*s = qRgba(QMIN(qRound(cc*k), 255), QMIN(qRound(cm*k), 255), QMIN(qRound(cy*k), 255), QMIN(qRound(ck*k), 255));
			}
			else
			{
				int k = 255 - QMIN(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r)), 255);
				int cc2, cm2, cy2;
				tmpR.setRgb(cc, cm, cy);
				tmpR.hsv(&hu, &sa, &v);
				if (cc == cm && cm == cy)
					tmpR.setHsv(hu, sa, 255 - ((255 - v) * k / 255));
				else
					tmpR.setHsv(hu, sa * k / 255, v);
				tmpR.getRgb(&cc2, &cm2, &cy2);
				int a = qAlpha(r);
				*s = qRgba(cc2, cm2, cy2, a);
			}
			s++;
		}
	}
}

void ScImage::invert(bool cmyk)
{
	int h = height();
	int w = width();
	for( int yi=0; yi < h; ++yi )
	{
		QRgb * s = (QRgb*)(scanLine( yi ));
		for( int xi=0; xi < w; ++xi )
		{
			if (cmyk)
			{
				unsigned char *p = (unsigned char *) s;
				unsigned char c, m, y, k;
				c = 255 - QMIN(255, p[0] + p[3]);
				m = 255 - QMIN(255, p[1] + p[3]);
				y = 255 - QMIN(255, p[2] + p[3]);
				k = QMIN(QMIN(c, m), y);
				p[0] = c - k;
				p[1] = m - k;
				p[2] = y - k;
				p[3] = k;
			}
			else
				*s ^= 0x00ffffff;
			s++;
		}
	}
}

void ScImage::toGrayscale(bool cmyk)
{
	int h = height();
	int w = width();
	for( int yi=0; yi < h; ++yi )
	{
		QRgb * s = (QRgb*)(scanLine( yi ));
		for( int xi=0; xi < w; ++xi )
		{
			QRgb r=*s;
			int k;
			if (cmyk)
			{
				k = QMIN(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r) + qAlpha(r)), 255);
				*s = qRgba(0, 0, 0, k);
			}
			else
			{
				k = QMIN(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r)), 255);
				int a = qAlpha(r);
				*s = qRgba(k, k, k, a);
			}
			s++;
		}
	}
}

void ScImage::swapRGBA()
{
	for (int i = 0; i < height(); ++i)
	{
		unsigned int *ptr = (QRgb *) scanLine(i);
		unsigned char r, g, b, a;
		for (int j = 0; j < width(); ++j)
		{
			r = qRed(*ptr);
			g = qGreen(*ptr);
			b = qBlue(*ptr);
			a = qAlpha(*ptr);
			*ptr++ = qRgba(b,g,r,a);
		}
	}
}


void ScImage::swapByteOrder(int one, int two, int three, int four)
{
	if (one >= 0)
	{
		for (int i = 0; i < height(); ++i)
		{
			unsigned char *ptr = (unsigned char *) scanLine(i);
			unsigned char val[4];
			for (int j = 0; j < width(); ++j)
			{			
				val[0] = ptr[one];
				val[1] = ptr[two];
				val[2] = ptr[three];
				val[3] = ptr[four];
				*ptr++ = val[0];
				*ptr++ = val[1];
				*ptr++ = val[2];
				*ptr++ = val[3];
			}
		}
	}
	else
	{
		// debug: make stripes 
		// one: -1=forward -2=backward
		// two: 0=hor, 1=vert
		// three: width of stripe
		// four: shift in permutation list
		
		static uint perm[24][4] = { { 0,1,2,3 }, { 0,1,3,2 }, { 0,2,1,3 }, { 0,2,3,1 }, { 0,3,1,2 }, { 0,3,2,1 },
		{ 1,0,2,3 }, { 1,0,3,2 }, { 1,2,0,3 }, { 1,2,3,0 }, { 1,3,0,2 }, { 1,3,2,0 },
		{ 2,1,0,3 }, { 2,1,3,0 }, { 2,0,1,3 }, { 2,0,3,1 }, { 2,3,1,0 }, { 2,3,0,1 },
		{ 3,1,2,0 }, { 3,1,0,2 }, { 3,2,1,0 }, { 3,2,0,1 }, { 3,0,1,2 }, { 3,0,2,1 } };
		
		for (int i = 0; i < height(); ++i)
		{
			unsigned char *ptr = (unsigned char *) scanLine(i);
			unsigned char val[4];
			for (int j = 0; j < width(); ++j)
			{			
				uint *per = perm[ ((two?j:i) / three + four) % 24 ];
				if (one == -1) {
					val[0] = ptr[per[0]];
					val[1] = ptr[per[1]];
					val[2] = ptr[per[2]];
					val[3] = ptr[per[3]];
				} else {
					val[per[0]] = ptr[0];
					val[per[1]] = ptr[1];
					val[per[2]] = ptr[2];
					val[per[3]] = ptr[3];
				}
				*ptr++ = val[0];
				*ptr++ = val[1];
				*ptr++ = val[2];
				*ptr++ = val[3];
			}
		}
	}
}


void ScImage::createLowRes(double scale)
{
	int w = qRound(width() / scale);
	int h = qRound(height() / scale);
	QImage tmp = smoothScale(w, h);
	create(w, h, 32);
	for( int yi=0; yi < tmp.height(); ++yi )
	{
		QRgb *s = (QRgb*)(tmp.scanLine( yi ));
		QRgb *d = (QRgb*)(scanLine( yi ));
		for(int xi=0; xi < tmp.width(); ++xi )
		{
			(*d) = (*s);
			s++;
			d++;
		}
	}
}

void ScImage::Convert2JPG(QString fn, int Quality, bool isCMYK, bool isGray)
{
	struct jpeg_compress_struct cinfo;
	struct my_error_mgr         jerr;
	FILE     *outfile;
	JSAMPROW row_pointer[1];
	row_pointer[0] = 0;
	cinfo.err = jpeg_std_error (&jerr.pub);
	jerr.pub.error_exit = my_error_exit;
	outfile = NULL;
	if (setjmp (jerr.setjmp_buffer))
	{
		jpeg_destroy_compress (&cinfo);
		if (outfile)
			fclose (outfile);
		return;
	}
	jpeg_create_compress (&cinfo);
	if ((outfile = fopen (fn.local8Bit(), "wb")) == NULL)
		return;
	jpeg_stdio_dest (&cinfo, outfile);
	cinfo.image_width  = width();
	cinfo.image_height = height();
	if (isCMYK)
	{
		cinfo.in_color_space = JCS_CMYK;
		cinfo.input_components = 4;
	}
	else
	{
		if (isGray)
		{
			cinfo.in_color_space = JCS_GRAYSCALE;
			cinfo.input_components = 1;
		}
		else
		{
			cinfo.in_color_space = JCS_RGB;
			cinfo.input_components = 3;
		}
	}
	jpeg_set_defaults (&cinfo);
	int qual[] = { 95, 85, 75, 50, 25 };  // These are the JPEG Quality settings 100 means best, 0 .. don't discuss
	jpeg_set_quality (&cinfo, qual[Quality], true);
	jpeg_start_compress (&cinfo, true);
	row_pointer[0] = new uchar[cinfo.image_width*cinfo.input_components];
	int w = cinfo.image_width;
	while (cinfo.next_scanline < cinfo.image_height)
	{
		uchar *row = row_pointer[0];
		if (isCMYK)
		{
			QRgb* rgba = (QRgb*)scanLine(cinfo.next_scanline);
			for (int i=0; i<w; ++i)
			{
				*row++ = 255-qRed(*rgba);
				*row++ = 255-qGreen(*rgba);
				*row++ = 255-qBlue(*rgba);
				*row++ = 255-qAlpha(*rgba);
				++rgba;
			}
		}
		else
		{
			if (isGray)
			{
				QRgb* rgba = (QRgb*)scanLine(cinfo.next_scanline);
				for (int i=0; i<w; ++i)
				{
					*row++ = qRed(*rgba);
					++rgba;
				}
			}
			else
			{
				QRgb* rgb = (QRgb*)scanLine(cinfo.next_scanline);
				for (int i=0; i<w; i++)
				{
					*row++ = qRed(*rgb);
					*row++ = qGreen(*rgb);
					*row++ = qBlue(*rgb);
					++rgb;
				}
			}
		}
		jpeg_write_scanlines (&cinfo, row_pointer, 1);
	}
	jpeg_finish_compress (&cinfo);
	fclose (outfile);
	jpeg_destroy_compress (&cinfo);
	delete [] row_pointer[0];
}

QString ScImage::ImageToTxt()
{
	int i = 0;
	int h = height();
	int w = width();
	unsigned char u;
	QString ImgStr = "";
	ImgStr.reserve(3 * h * w);
	for( int yi=0; yi < h; ++yi )
	{
		QRgb * s = (QRgb*)(scanLine( yi ));
		for( int xi=0; xi < w; ++xi )
		{
			QRgb r=*s++;
			u=qRed(r);
			ImgStr[i++] = u;
			u=qGreen(r);
			ImgStr[i++] = u;
			u=qBlue(r);
			ImgStr[i++] = u;
		}
	}
	return ImgStr;
}

QString ScImage::ImageToGray()
{
	int i = 0;
	int h = height();
	int w = width();
	QString ImgStr = "";
	ImgStr.reserve( h * w);
	for( int yi=0; yi < h; ++yi )
	{
		QRgb * s = (QRgb*)(scanLine( yi ));
		for( int xi=0; xi < w; ++xi )
		{
			QRgb r=*s;
			int k = QMIN(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r)), 255);
			*s = qRgba(k, 0, 0, 0);
			ImgStr[i++] = k;
			s++;
		}
	}
	return ImgStr;
}

QString ScImage::ImageToCMYK_PDF(bool pre)
{
	int i = 0;
	int h = height();
	int w = width();
	QString ImgStr = "";
	ImgStr.reserve( 4 * h * w );
	if (pre)
	{
		for( int yi=0; yi < h; ++yi )
		{
			QRgb * s = (QRgb*)(scanLine( yi ));
			for( int xi=0; xi < w; ++xi )
			{
				QRgb r=*s;
				int c = qRed(r);
				int m = qGreen(r);
				int y = qBlue(r);
				int k = qAlpha(r);
				/*				*s = qRgba(m, y, k, c); */
				ImgStr[i++] = static_cast<unsigned char> (c);
				ImgStr[i++] = static_cast<unsigned char> (m);
				ImgStr[i++] = static_cast<unsigned char> (y);
				ImgStr[i++] = static_cast<unsigned char> (k);
				s++;
			}
		}
	}
	else
	{
		for( int yi=0; yi < h; ++yi )
		{
			QRgb * s = (QRgb*)(scanLine( yi ));
			for( int xi=0; xi < w; ++xi )
			{
				QRgb r=*s;
				int c = 255 - qRed(r);
				int m = 255 - qGreen(r);
				int y = 255 - qBlue(r);
				int k = QMIN(QMIN(c, m), y);
				//				*s = qRgba(m, y, k, c);
//				*s = qRgba(c, m, y, k);
				ImgStr[i++] = static_cast<unsigned char> (c - k);
				ImgStr[i++] = static_cast<unsigned char> (m - k);
				ImgStr[i++] = static_cast<unsigned char> (y - k);
				ImgStr[i++] = static_cast<unsigned char> (k);
				s++;
			}
		}
	}
	return ImgStr;
}

QString ScImage::ImageToCMYK_PS(int pl, bool pre)
{
	int i = 0;
	int h = height();
	int w = width();
	QString ImgStr = "";
	if(pl == -1)
		ImgStr.reserve(4 * h * w);
	else
		ImgStr.reserve(h * w);
	if (pre)
	{
		for( int yi=0; yi < h; ++yi )
		{
			QRgb * s = (QRgb*)(scanLine( yi ));
			for( int xi=0; xi < w; ++xi )
			{
				QRgb r=*s++;
				int c = qRed(r);
				int m = qGreen(r);
				int y = qBlue(r);
				int k = qAlpha(r);
				if (pl == -1)
				{
					ImgStr[i++] = static_cast<unsigned char> (c);
					ImgStr[i++] = static_cast<unsigned char> (m);
					ImgStr[i++] = static_cast<unsigned char> (y);
					ImgStr[i++] = static_cast<unsigned char> (k);
				}
				else
				{
					if (pl == -2)
						ImgStr[i++] = static_cast<unsigned char> (QMIN(255, qRound(0.3 * c + 0.59 * m + 0.11 * y + k)));
					if (pl == 1)
						ImgStr[i++] = static_cast<unsigned char> (c);
					if (pl == 2)
						ImgStr[i++] = static_cast<unsigned char> (m);
					if (pl == 3)
						ImgStr[i++] = static_cast<unsigned char> (y);
					if (pl == 0)
						ImgStr[i++] = static_cast<unsigned char> (k);
				}
			}
		}
	}
	else
	{
		for( int yi=0; yi < h; ++yi )
		{
			QRgb * s = (QRgb*)(scanLine( yi ));
			for( int xi=0; xi < w; ++xi )
			{
				QRgb r=*s++;
				int c = 255 - qRed(r);
				int m = 255 - qGreen(r);
				int y = 255 - qBlue(r);
				int k = QMIN(QMIN(c, m), y);
				if (pl == -1)
				{
					ImgStr[i++] = static_cast<unsigned char> (c - k);
					ImgStr[i++] = static_cast<unsigned char> (m - k);
					ImgStr[i++] = static_cast<unsigned char> (y - k);
					ImgStr[i++] = static_cast<unsigned char> (k);
				}
				else
				{
					if (pl == -2)
						ImgStr[i++] = static_cast<unsigned char> (QMIN(255, qRound(0.3 * c + 0.59 * m + 0.11 * y + k)));
					if (pl == 1)
						ImgStr[i++] = static_cast<unsigned char> (c - k);
					if (pl == 2)
						ImgStr[i++] = static_cast<unsigned char> (m - k);
					if (pl == 3)
						ImgStr[i++] = static_cast<unsigned char> (y - k);
					if (pl == 0)
						ImgStr[i++] = static_cast<unsigned char> (k);
				}
			}
		}
	}
	return ImgStr;
}

// Check that the header is a valid PSD.
bool ScImage::IsValid( const PSDHeader & header )
{
	if( header.signature != 0x38425053 )
		return false;
	return true;
}

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

unsigned char ScImage::INT_MULT ( unsigned char a, unsigned char b )
{
	int c = a * b + 0x80;
	return (unsigned char)(( ( c >> 8 ) + c ) >> 8);
}

/*!
 * Convert a color in RGB space to HSV space (Hue, Saturation, Value).
 * \param red the red component (modified in place).
 * \param green the green component (modified in place).
 * \param blue the blue component (modified in place).
 */
void ScImage::RGBTOHSV ( uchar& red, uchar& green, uchar& blue )
{
	int r, g, b;
	double h, s, v;
	int min, max;
	h = 0.;
	r = red;
	g = green;
	b = blue;
	if ( r > g )
	{
		max = QMAX( r, b );
		min = QMIN( g, b );
	}
	else
	{
		max = QMAX( g, b );
		min = QMIN( r, b );
	}
	v = max;
	if ( max != 0 )
		s = ( ( max - min ) * 255 ) / (double)max;
	else
		s = 0;
	if ( s == 0 )
		h = 0;
	else
	{
		int delta = max - min;
		if ( r == max )
			h = ( g - b ) / (double)delta;
		else if ( g == max )
			h = 2 + ( b - r ) / (double)delta;
		else if ( b == max )
			h = 4 + ( r - g ) / (double)delta;
		h *= 42.5;
		if ( h < 0 )
			h += 255;
		if ( h > 255 )
			h -= 255;
	}
	red   = (uchar)h;
	green = (uchar)s;
	blue  = (uchar)v;
}

/*!
 * Convert a color in HSV space to RGB space.
 * \param hue the hue component (modified in place).
 * \param saturation the saturation component (modified in place).
 * \param value the value component (modified in place).
 */
void ScImage::HSVTORGB ( uchar& hue, uchar& saturation, uchar& value )
{
	if ( saturation == 0 )
	{
		hue        = value;
		saturation = value;
		value      = value;
	}
	else
	{
		double h = hue * 6. / 255.;
		double s = saturation / 255.;
		double v = value / 255.;

		double f = h - (int)h;
		double p = v * ( 1. - s );
		double q = v * ( 1. - ( s * f ) );
		double t = v * ( 1. - ( s * ( 1. - f ) ) );
		// Worth a note here that gcc 2.96 will generate different results
		// depending on optimization mode on i386.
		switch ((int)h)
		{
		case 0:
			hue        = (uchar)( v * 255 );
			saturation = (uchar)( t * 255 );
			value      = (uchar)( p * 255 );
			break;
		case 1:
			hue        = (uchar)( q * 255 );
			saturation = (uchar)( v * 255 );
			value      = (uchar)( p * 255 );
			break;
		case 2:
			hue        = (uchar)( p * 255 );
			saturation = (uchar)( v * 255 );
			value      = (uchar)( t * 255 );
			break;
		case 3:
			hue        = (uchar)( p * 255 );
			saturation = (uchar)( q * 255 );
			value      = (uchar)( v * 255 );
			break;
		case 4:
			hue        = (uchar)( t * 255 );
			saturation = (uchar)( p * 255 );
			value      = (uchar)( v * 255 );
			break;
		case 5:
			hue        = (uchar)( v * 255 );
			saturation = (uchar)( p * 255 );
			value      = (uchar)( q * 255 );
		}
	}
}

/*!
 * Convert a color in RGB space to HLS space (Hue, Lightness, Saturation).
 * \param red the red component (modified in place).
 * \param green the green component (modified in place).
 * \param blue the blue component (modified in place).
 */
void ScImage::RGBTOHLS ( uchar& red, uchar& green, uchar& blue )
{
	int r = red;
	int g = green;
	int b = blue;
	int min, max;
	if ( r > g )
	{
		max = QMAX( r, b );
		min = QMIN( g, b );
	}
	else
	{
		max = QMAX( g, b );
		min = QMIN( r, b );
	}
	double h;
	double l = ( max + min ) / 2.;
	double s;
	if ( max == min )
	{
		s = 0.;
		h = 0.;
	}
	else
	{
		int delta = max - min;
		if ( l < 128 )
			s = 255 * (double)delta / (double)( max + min );
		else
			s = 255 * (double)delta / (double)( 511 - max - min );
		if ( r == max )
			h = ( g - b ) / (double)delta;
		else if ( g == max )
			h = 2 + ( b - r ) / (double)delta;
		else
			h = 4 + ( r - g ) / (double)delta;
		h *= 42.5;
		if ( h < 0 )
			h += 255;
		else if ( h > 255 )
			h -= 255;
	}
	red   = (uchar)h;
	green = (uchar)l;
	blue  = (uchar)s;
}

/*!
 * Implement the HLS "double hex-cone".
 * \param n1 lightness fraction (?)
 * \param n2 saturation fraction (?)
 * \param hue hue "angle".
 * \return HLS value.
 */
int ScImage::HLSVALUE ( double n1, double n2, double hue )
{
	double value;
	if ( hue > 255 )
		hue -= 255;
	else if ( hue < 0 )
		hue += 255;
	if ( hue < 42.5 )
		value = n1 + ( n2 - n1 ) * ( hue / 42.5 );
	else if ( hue < 127.5 )
		value = n2;
	else if ( hue < 170 )
		value = n1 + ( n2 - n1 ) * ( ( 170 - hue ) / 42.5 );
	else
		value = n1;
	return (int)( value * 255 );
}

/*!
 * Convert a color in HLS space to RGB space.
 * \param hue the hue component (modified in place).
 * \param lightness the lightness component (modified in place).
 * \param saturation the saturation component (modified in place).
 */
void ScImage::HLSTORGB ( uchar& hue, uchar& lightness, uchar& saturation )
{
	double h = hue;
	double l = lightness;
	double s = saturation;
	if ( s == 0 )
	{
		hue        = (uchar)l;
		lightness  = (uchar)l;
		saturation = (uchar)l;
	}
	else
	{
		double m1, m2;
		if ( l < 128 )
			m2 = ( l * ( 255 + s ) ) / 65025.;
		else
			m2 = ( l + s - ( l * s ) / 255. ) / 255.;
		m1 = ( l / 127.5 ) - m2;
		hue        = HLSVALUE( m1, m2, h + 85 );
		lightness  = HLSVALUE( m1, m2, h );
		saturation = HLSVALUE( m1, m2, h - 85 );
	}
}

bool ScImage::loadChannel( QDataStream & s, const PSDHeader & header, QValueList<PSDLayer> &layerInfo, uint layer, int channel, int component, QImage &tmpImg)
{
	uint base = s.device()->at();
	uchar cbyte;
	ushort compression;
	s >> compression;
	if( compression > 1 )
		return false;
	if (compression == 0)
	{
		int count = layerInfo[layer].channelLen[channel]-2;
		for (int i = 0; i < tmpImg.height(); i++)
		{
			uchar *ptr =  tmpImg.scanLine(i);
			for (int j = 0; j < tmpImg.width(); j++)
			{
				s >> cbyte;
				count--;
				if (header.color_mode == CM_CMYK)
					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_INDEXED) && (component != 3))
				{
					int ccol = colorTable[cbyte];
					ptr[2] = qRed(ccol);
					ptr[1] = qGreen(ccol);
					ptr[0] = qBlue(ccol);
				}
				else
				{
					if (channel < 4)
						ptr[component] = cbyte;
				}
				if (count == 0)
					break;
				ptr += 4;
			}
			if (count == 0)
				break;
		}
	}
	else
	{
		s.device()->at( s.device()->at() + tmpImg.height() * 2 );
		uint pixel_count = tmpImg.width();
		for (int hh = 0; hh < tmpImg.height(); hh++)
		{
			uint count = 0;
			uchar *ptr = tmpImg.scanLine(hh);
			uchar *ptr2 = ptr+tmpImg.width() * 4;
			ptr += component;
			while( count < pixel_count )
			{
				uchar c;
				if(s.atEnd())
					return false;
				s >> c;
				uint 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)
								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_INDEXED) && (component != 3))
							{
								ptr -= component;
								int ccol = colorTable[cbyte];
								ptr[2] = qRed(ccol);
								ptr[1] = qGreen(ccol);
								ptr[0] = qBlue(ccol);
								ptr += component;
							}
							else
							{
								*ptr = cbyte;
							}
						}
						ptr += 4;
						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)
						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_INDEXED) && (component != 3))
							{
								ptr -= component;
								int ccol = colorTable[val];
								ptr[2] = qRed(ccol);
								ptr[1] = qGreen(ccol);
								ptr[0] = qBlue(ccol);
								ptr += component;
							}
							else
								*ptr = val;
						}
						ptr += 4;
						len--;
					}
				}
				else if( len == 128 )
				{
					// No-op.
				}
			}
		}
	}
	s.device()->at( base+layerInfo[layer].channelLen[channel] );
	return true;
}

bool ScImage::loadLayerChannels( QDataStream & s, const PSDHeader & header, QValueList<PSDLayer> &layerInfo, uint layer, bool* firstLayer)
{
	// Find out if the data is compressed.
	// Known values:
	//   0: no compression
	//   1: RLE compressed
	uint base = s.device()->at();
	uint base2 = base;
	uint channel_num = layerInfo[layer].channelLen.count();
	QImage tmpImg = QImage();
	QImage mask = QImage();
	if( !tmpImg.create( layerInfo[layer].width, layerInfo[layer].height, 32 ))
	{
		for(uint channel = 0; channel < channel_num; channel++)
		{
			base2 += layerInfo[layer].channelLen[channel];
		}
		s.device()->at( base2 );
		return false;
	}
	tmpImg.fill(qRgba(255, 255, 255, 0));
	tmpImg.setAlphaBuffer( true );
	channel_num = QMIN(channel_num, 39);
	uint components[40];
	for(uint channel = 0; channel < channel_num; channel++)
	{
		switch(layerInfo[layer].channelType[channel])
		{
		case 0:
			components[channel] = 2;
			break;
		case 1:
			components[channel] = 1;
			break;
		case 2:
			components[channel] = 0;
			break;
		case 3:
			components[channel] = 3;
			break;
		case -1:
		case -2:
			components[channel] = 3;
			break;
		}
	}
	if (channel_num < 4)
	{
		for (int i = 0; i < tmpImg.height(); i++)
		{
			QRgb * s = (QRgb*)(tmpImg.scanLine( i ));
			for (int j = 0; j < tmpImg.width(); j++)
			{
				*s++ = qRgba(255, 255, 255, 255);
			}
		}
	}
	for(uint channel = 0; channel < channel_num; channel++)
	{
		if ((layerInfo[layer].channelType[channel] < 0) && (header.color_mode == CM_CMYK))
		{
			s.device()->at( base+layerInfo[layer].channelLen[channel] );
			base = base+layerInfo[layer].channelLen[channel];
			continue;
		}
//		if ((layerInfo[layer].channelType[channel] == -2) || (layerInfo[layer].channelType[channel] == -1))
		if (layerInfo[layer].channelType[channel] == -2)
		{
			if (!mask.create( layerInfo[layer].maskWidth, layerInfo[layer].maskHeight, 32 ))
//			{
//				if (!mask.create(layerInfo[layer].width, layerInfo[layer].height, 32 ))
					break;
//			}
			mask.fill(qRgba(255, 255, 255, 0));
			mask.setAlphaBuffer( true );
			if (!loadChannel(s, header, layerInfo, layer, channel, components[channel], mask))
				break;
		}
		else
		{
			if (!loadChannel(s, header, layerInfo, layer, channel, components[channel], tmpImg))
				break;
		}
	}
	for(uint channel = 0; channel < channel_num; channel++)
	{
		base2 += layerInfo[layer].channelLen[channel];
	}
	s.device()->at( base2 );
	QImage tmpImg2 = tmpImg.copy();
	if (header.color_mode == CM_CMYK)
	{
		for (int i = 0; i < tmpImg.height(); i++)
		{
			unsigned int *ptr = (unsigned int *) tmpImg.scanLine(i);
			unsigned int *ptr2 = (unsigned int *) tmpImg2.scanLine(i);
			unsigned char r, g, b;
			for (int j = 0; j < tmpImg.width(); j++)
			{
				unsigned char *p = (unsigned char *) ptr;
				unsigned char *p2 = (unsigned char *) ptr2;
				r = 255 - QMIN(255, p[0] + p[3]);
				g = 255 - QMIN(255, p[1] + p[3]);
				b = 255 - QMIN(255, p[2] + p[3]);
				p2[0] = r;
				p2[1] = g;
				p2[2] = b;
				p2[3] = 255;
				ptr++;
				ptr2++;
			}
		}
	}
	QImage imt;
	double sx = tmpImg.width() / 40.0;
	double sy = tmpImg.height() / 40.0;
	imt = sy < sx ?  tmpImg2.smoothScale(qRound(tmpImg.width() / sx), qRound(tmpImg.height() / sx)) :
	      tmpImg2.smoothScale(qRound(tmpImg.width() / sy), qRound(tmpImg.height() / sy));
	layerInfo[layer].thumb = imt.copy();
	bool visible = !(layerInfo[layer].flags & 2);
	if ((imgInfo.isRequest) && (imgInfo.RequestProps.contains(layer)))
		visible = imgInfo.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 startSrcYm, startSrcXm, 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 ((imgInfo.isRequest) && (imgInfo.RequestProps.contains(layer)))
			layBlend2 = imgInfo.RequestProps[layer].blend;
		if ((layBlend2 == "diss") && (header.color_mode != CM_CMYK))
		{
			for (int l = 0; l < tmpImg.height(); l++)
			{
				srand(random_table[ l  % 4096]);
				for (int k = 0; k < tmpImg.width(); k++)
				{
					int rand_val = rand() & 0xff;
					QRgb pixel = tmpImg.pixel(k, l);
					if (rand_val > 128)
						tmpImg.setPixel(k, l, qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), 0));
				}
			}
		}
		if (*firstLayer)
		{
			for( int yi=static_cast<int>(startSrcY); yi < QMIN(tmpImg.height(), height()); ++yi )
			{
				QRgb *s = (QRgb*)(tmpImg.scanLine( yi ));
				QRgb *d = (QRgb*)(scanLine( QMIN(static_cast<int>(startDstY), height()-1) ));
				d += QMIN(static_cast<int>(startDstX), width()-1);
				s += QMIN(static_cast<int>(startSrcX), tmpImg.width()-1);
/*				unsigned int *srcm;
				if (!mask.isNull())
				{
					srcm = (unsigned int *)mask.scanLine(QMIN(yi, mask.height()-1));
					srcm += QMIN(static_cast<int>(startSrcXm), mask.width()-1);
				} */
				for(int xi=static_cast<int>(startSrcX); xi < QMIN(tmpImg.width(), width()); ++xi )
				{
					(*d) = (*s);
/*					unsigned char *sm = (unsigned char *) srcm;
					unsigned char *sc = (unsigned char *) d;
					unsigned char *ss = (unsigned char *) s;
					if (!mask.isNull())
					{
						if (header.color_mode == CM_CMYK)
						{
							sc[0] = (sc[0] * (255 - sm[3]) + ss[0] * sm[3]) / 255;
							sc[1] = (sc[1] * (255 - sm[3]) + ss[1] * sm[3]) / 255;
							sc[2] = (sc[2] * (255 - sm[3]) + ss[2] * sm[3]) / 255;
							sc[3] = (sc[3] * (255 - sm[3]) + ss[3] * sm[3]) / 255;
						}
						else
							sc[3] = sm[3];
					}
					srcm++; */
					s++;
					d++;
				}
				startDstY++;
			}
		}
		else
		{
			for (int i = static_cast<int>(startSrcY); i < layerInfo[layer].height; i++)
			{
				unsigned int *dst = (unsigned int *)scanLine(QMIN(static_cast<int>(startDstY), height()-1));
				unsigned int *src = (unsigned int *)tmpImg.scanLine(QMIN(i, tmpImg.height()-1));
				dst += QMIN(static_cast<int>(startDstX), width()-1);
				src += QMIN(static_cast<int>(startSrcX), tmpImg.width()-1);
				unsigned int *srcm = 0;
				if (!mask.isNull())
				{
					srcm = (unsigned int *)mask.scanLine(QMIN(i, mask.height()-1));
					srcm += QMIN(static_cast<int>(startSrcXm), mask.width()-1);
				}
				startDstY++;
				unsigned char r, g, b, a, src_r, src_g, src_b, src_a, mask_a;
				//<<#3356
				unsigned int maxDestX = width() - startDstX + startSrcX - 1;
				for (unsigned int j = startSrcX; j < QMIN(maxDestX, static_cast<unsigned int>(layerInfo[layer].width)); j++)
				//for (unsigned int j = startSrcX; j < static_cast<unsigned int>(layerInfo[layer].width); j++)
				//>>#3356
				{
					unsigned char *d = (unsigned char *) dst;
					unsigned char *s = (unsigned char *) src;
					unsigned char *sm = (unsigned char *) srcm;
					src_r = s[0];
					src_g = s[1];
					src_b = s[2];
					src_a = s[3];
					if (!mask.isNull())
						mask_a = sm[3];
					QString layBlend = layerInfo[layer].blend;
					if ((imgInfo.isRequest) && (imgInfo.RequestProps.contains(layer)))
						layBlend = imgInfo.RequestProps[layer].blend;
					if (layBlend == "mul ")
					{
						if (header.color_mode == CM_CMYK)
						{
							src_r = INT_MULT(src_r, d[0]);
							src_g = INT_MULT(src_g, d[1]);
							src_b = INT_MULT(src_b, d[2]);
							src_a = INT_MULT(src_a, d[3]);
						}
						else
						{
							if (d[3] > 0)
							{
								src_r = INT_MULT(src_r, d[0]);
								src_g = INT_MULT(src_g, d[1]);
								src_b = INT_MULT(src_b, d[2]);
							}
						}
					}
					else if (layBlend == "scrn")
					{
						if (header.color_mode == CM_CMYK)
						{
							src_r = 255 - INT_MULT(255 - d[0], 255 - src_r);
							src_g = 255 - INT_MULT(255 - d[1], 255 - src_g);
							src_b = 255 - INT_MULT(255 - d[2], 255 - src_b);
							src_a = 255 - INT_MULT(255 - d[3], 255 - src_a);
						}
						else
						{
							if (d[3] > 0)
							{
								src_r = 255 - INT_MULT(255 - d[0], 255 - src_r);
								src_g = 255 - INT_MULT(255 - d[1], 255 - src_g);
								src_b = 255 - INT_MULT(255 - d[2], 255 - src_b);
							}
						}
					}
					else if (layBlend == "over")
					{
						if (header.color_mode == CM_CMYK)
						{
							src_r = INT_MULT(d[0], d[0] + INT_MULT(2 * src_r, 255 - d[0]));
							src_g = INT_MULT(d[1], d[1] + INT_MULT(2 * src_g, 255 - d[1]));
							src_b = INT_MULT(d[2], d[2] + INT_MULT(2 * src_b, 255 - d[2]));
							src_a = INT_MULT(d[3], d[3] + INT_MULT(2 * src_a, 255 - d[3]));
						}
						else
						{
							if (d[3] > 0)
							{
								src_r = INT_MULT(d[0], d[0] + INT_MULT(2 * src_r, 255 - d[0]));
								src_g = INT_MULT(d[1], d[1] + INT_MULT(2 * src_g, 255 - d[1]));
								src_b = INT_MULT(d[2], d[2] + INT_MULT(2 * src_b, 255 - d[2]));
							}
						}
					}
					else if (layBlend == "diff")
					{
						if (header.color_mode == CM_CMYK)
						{
							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];
							src_a = d[3] > src_a ? d[3] - src_a : src_a - d[3];
						}
						else
						{
							if (d[3] > 0)
							{
								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];
							}
						}
					}
					else if (layBlend == "dark")
					{
						if (header.color_mode == CM_CMYK)
						{
							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;
							src_a = d[3] < src_a ? d[3] : src_a;
						}
						else
						{
							if (d[3] > 0)
							{
								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;
							}
						}
					}
					else if (layBlend == "hLit")
					{
						if (header.color_mode == CM_CMYK)
						{
							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);
							src_a = src_a < 128 ? src_a * d[3] / 128 : 255 - ((255-src_a) * (255-d[3]) / 128);
						}
						else
						{
							if (d[3] > 0)
							{
								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);
							}
						}
					}
					else if (layBlend == "sLit")
					{
						if (header.color_mode == CM_CMYK)
						{
							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;
							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 (d[3] > 0)
							{
								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;
							}
						}
					}
					else if (layBlend == "lite")
					{
						if (header.color_mode == CM_CMYK)
						{
							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];
							src_a = d[3] < src_a ? src_a : d[3];
						}
						else
						{
							if (d[3] > 0)
							{
								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];
							}
						}
					}
					else if (layBlend == "smud")
					{
						if (header.color_mode == CM_CMYK)
						{
							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;
							src_a = d[3] + src_a - src_a * d[3] / 128;
						}
						else
						{
							if (d[3] > 0)
							{
								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;
							}
						}
					}
					else if (layBlend == "div ")
					{
						if (header.color_mode == CM_CMYK)
						{
							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);
							src_a = src_a == 255 ? 255 : ((d[3] * 256) / (255-src_a)) > 255 ? 255 : (d[3] * 256) / (255-src_a);
						}
						else
						{
							if (d[3] > 0)
							{
								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);
							}
						}
					}
					else if (layBlend == "idiv")
					{
						if (header.color_mode == CM_CMYK)
						{
							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);
							src_a = src_a == 0 ? 0 : (255 - (((255-d[3]) * 256) / src_a)) < 0 ? 0 : 255 - (((255-d[3]) * 256) / src_a);
						}
						else
						{
							if (d[3] > 0)
							{
								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);
							}
						}
					}
					else if (layBlend == "hue ")
					{
						if (header.color_mode != CM_CMYK)
						{
							if (d[3] > 0)
							{
								uchar new_r = d[0];
								uchar new_g = d[1];
								uchar 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 == "sat ")
					{
						if (header.color_mode != CM_CMYK)
						{
							if (d[3] > 0)
							{
								uchar new_r = d[0];
								uchar new_g = d[1];
								uchar 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 == "lum ")
					{
						if (header.color_mode != CM_CMYK)
						{
							if (d[3] > 0)
							{
								uchar new_r = d[0];
								uchar new_g = d[1];
								uchar 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 == "colr")
					{
						if (header.color_mode != CM_CMYK)
						{
							if (d[3] > 0)
							{
								uchar new_r = d[0];
								uchar new_g = d[1];
								uchar 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;
							}
						}
					}
					
					int layOpa = layerInfo[layer].opacity;
					if ((imgInfo.isRequest) && (imgInfo.RequestProps.contains(layer)))
						layOpa = imgInfo.RequestProps[layer].opacity;
					if (!mask.isNull())
						layOpa = INT_MULT(mask_a, layOpa);
					if (header.color_mode != CM_CMYK)
						src_a = INT_MULT(src_a, layOpa);
					if (d[3] > 0)
					{
						r = (d[0] * (255 - layOpa) + src_r * layOpa) / 255;
						g = (d[1] * (255 - layOpa) + src_g * layOpa) / 255;
						b = (d[2] * (255 - layOpa) + src_b * layOpa) / 255;
						if (header.color_mode == CM_CMYK)
							a = (d[3] * (255 - layOpa) + src_a * layOpa) / 255;
						else
						{
							a = d[3] + INT_MULT(255 - d[3], src_a);
							r = (d[0] * (255 - src_a) + src_r * src_a) / 255;
							g = (d[1] * (255 - src_a) + src_g * src_a) / 255;
							b = (d[2] * (255 - src_a) + src_b * src_a) / 255;
						}
					}
					else
					{
						r = src_r;
						g = src_g;
						b = src_b;
						a = src_a;
					}
					if (header.color_mode == CM_CMYK)
					{
						d[0] = r;
						d[1] = g;
						d[2] = b;
						d[3] = a;
					}
					else
					{
						if (src_a > 0)
						{
							d[0] = r;
							d[1] = g;
							d[2] = b;
							d[3] = a;
						}
					}
					dst++;
					src++;
					if (!mask.isNull())
						srcm++;
				}
			}
		}
	}
	*firstLayer = false;
	return true;
}

bool ScImage::loadLayer( QDataStream & s, const PSDHeader & header )
{
	// 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;
	bool systemBigEndian;
	int systemWordsize;
	qSysInfo( &systemWordsize, &systemBigEndian );
	if (channel_num < 4)
	{
		for (int i = 0; i < height(); i++)
		{
			QRgb * s = (QRgb*)(scanLine( i ));
			for (int j = 0; j < width(); j++)
			{
				*s++ = qRgba(0, 0, 0, 255);
			}
		}
	}
//	channel_num = 4;
	const uint pixel_count = header.height * header.width;
	uint components[4] = {2, 1, 0, 3};
	if (header.color_mode == CM_RGB) {
/*	dunno if this is needed -- av
		components[0] = 0;
		components[1] = 1;
		components[2] = 2;
		components[3] = 3;
*/
	}
	else if (systemBigEndian ) {
/*		components[0] = 1;
		components[1] = 2;
		components[2] = 3;
		components[3] = 0;
*/
	}
	if( compression )
	{
		// Skip row lengths.
		ushort w;
		for(uint i = 0; i < header.height * header.channel_count; i++)
		{
			s >> w;
		}
		// Read RLE data.
		for(uint channel = 0; channel < channel_num; channel++)
		{
			uchar * ptr = bits() + components[channel];
			uint count = 0;
			while( count < pixel_count )
			{
				uchar c;
				if(s.atEnd())
					return false;
				s >> c;
				uint 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)
							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_INDEXED) && (components[channel] != 3))
						{
							ptr -= components[channel];
							int ccol = colorTable[cbyte];
							ptr[2] = qRed(ccol);
							ptr[1] = qGreen(ccol);
							ptr[0] = qBlue(ccol);
							ptr += components[channel];
						}
						else
							*ptr = cbyte;
						ptr += 4;
						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)
						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_INDEXED) && (components[channel] != 3))
						{
							ptr -= components[channel];
							int ccol = colorTable[val];
							ptr[2] = qRed(ccol);
							ptr[1] = qGreen(ccol);
							ptr[0] = qBlue(ccol);
							ptr += components[channel];
						}
						else
							*ptr = val;
						ptr += 4;
						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.
		for(uint channel = 0; channel < channel_num; channel++)
		{
			uchar * ptr = bits() + components[channel];
			// Read the data.
			uint count = pixel_count;
			while( count != 0 )
			{
				s >> cbyte;
				if (header.color_mode == CM_CMYK)
					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_INDEXED) && (components[channel] != 3))
				{
					ptr -= components[channel];
					int ccol = colorTable[cbyte];
					ptr[2] = qRed(ccol);
					ptr[1] = qGreen(ccol);
					ptr[0] = qBlue(ccol);
					ptr += components[channel];
				}
				else
					*ptr = cbyte;
				ptr += 4;
				count--;
			}
		}
	}
	return true;
}

QString ScImage::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()->at( s.device()->at() + adj );
	return ret;
}

QString ScImage::getPascalString(QDataStream & s)
{
	uchar len, tmp;
	uint adj;
	QString ret = "";
	s >> len;
	if (len == 0)
	{
		s >> tmp;
		return ret;
	}
	for( int i = 0; i < len; i++ )
	{
		s >> tmp;
		ret += QChar(tmp);
	}
	adj = (ret.length()+1) % 2;
	s.device()->at( s.device()->at() + adj );
	return ret;
}

void ScImage::parseRessourceData( QDataStream & s, const PSDHeader & header, uint size )
{
	uint signature, resSize, offset, resBase, vRes, hRes, adj;
	ushort resID, hResUnit, vResUnit, dummyW;
	QString resName;
	uchar filler;
	offset = 0;
	bool first = false;
	bool pathOpen = false;
	FPoint firstPoint, firstControl;
	FPointArray clip2;
	while ((offset + 6)< size)
	{
		s >> signature;
		offset += 4;
		if(((signature >> 24)&0xff) != '8' ||
		        ((signature >> 16)&0xff) != 'B' ||
		        ((signature >> 8)&0xff) != 'I' ||
		        ((signature )&0xff) != 'M' )
			break;
		s >> resID;
		offset += 2;
		adj = s.device()->at();
		resName = getPascalString(s);
		offset += s.device()->at() - adj;
		s >> resSize;
		if(offset + resSize > size)
			break;
		resBase = s.device()->at();
		if ( (resID >= 0x07d0) && (resID <= 0x0bb6) )
		{
			QString db1, db2;
			short type;
			uint data1, data2, data3, data4, data5, data6;
			double frac1, frac2, frac3, frac4, frac5, frac6;
			ushort man1, man2, man3, man4, man5, man6;
			uint offset2;
			offset2 = 0;
			first = false;
			pathOpen = false;
			clip2.resize(0);
			while (offset2 < resSize)
			{
				s >> type;
				s >> data1;
				frac1 = (data1 & 0x00FFFFFF) / 16777215.0;
				man1 = (data1 & 0x0F000000) >> 24;
				frac1 = (frac1 + man1) * header.height;
				s >> data2;
				frac2 = (data2 & 0x00FFFFFF) / 16777215.0;
				man2 = (data2 & 0x0F000000) >> 24;
				frac2 = (frac2 + man2) * header.width;
				s >> data3;
				frac3 = (data3 & 0x00FFFFFF) / 16777215.0;
				man3 = (data3 & 0x0F000000) >> 24;
				frac3 = (frac3 + man3) * header.height;
				s >> data4;
				frac4 = (data4 & 0x00FFFFFF) / 16777215.0;
				man4 = (data4 & 0x0F000000) >> 24;
				frac4 = (frac4 + man4) * header.width;
				s >> data5;
				frac5 = (data5 & 0x00FFFFFF) / 16777215.0;
				man5 = (data5 & 0x0F000000) >> 24;
				frac5 = (frac5 + man5) * header.height;
				s >> data6;
				frac6 = (data6 & 0x00FFFFFF) / 16777215.0;
				man6 = (data6 & 0x0F000000) >> 24;
				frac6 = (frac6 + man6) * header.width;
				switch (type)
				{
				case 0:
				case 3:
					if (pathOpen)
					{
						clip2.addPoint(firstPoint);
						clip2.addPoint(firstControl);
						clip2.setMarker();
					}
					pathOpen = false;
					first = true;
					break;
				case 1:
				case 2:
				case 4:
				case 5:
					if (first)
					{
						firstControl = FPoint(frac2, frac1);
						firstPoint = FPoint(frac4, frac3);
						clip2.addPoint(FPoint(frac4, frac3));
						clip2.addPoint(FPoint(frac6, frac5));
					}
					else
					{
						clip2.addPoint(frac4, frac3);
						clip2.addPoint(frac2, frac1);
						clip2.addPoint(frac4, frac3);
						clip2.addPoint(frac6, frac5);
					}
					pathOpen = true;
					first = false;
					break;
				case 6:
					first = true;
					break;
				default:
					break;
				}
				offset2 += 26;
			}
			clip2.addPoint(firstPoint);
			clip2.addPoint(firstControl);
			imgInfo.PDSpathData.insert(resName, clip2.copy());
		}
		else
		{
			switch (resID)
			{
			case 0x0bb7:
				adj = s.device()->at();
				imgInfo.clipPath = getPascalString(s);
				offset += s.device()->at() - adj;
				break;
			case 0x03ed:
				s >> hRes;
				s >> hResUnit;
				s >> dummyW;
				s >> vRes;
				s >> vResUnit;
				s >> dummyW;
				imgInfo.xres = qRound(hRes / 65536.0);
				imgInfo.yres = qRound(vRes / 65536.0);
				break;
			case 0x040f:
				{
					icclen = resSize;
					char* buffer = (char*)malloc(resSize);
					iccbuf = buffer;
					s.readRawBytes(buffer, resSize);
				}
				break;
			case 0x0409:
			case 0x040C:
				{
					uint thdummy, thsize;
					s >> thdummy;
					s >> thdummy;
					s >> thdummy;
					s >> thdummy;
					s >> thdummy;
					s >> thsize;
					s >> thdummy;
					char* buffer = (char*)malloc(thsize);
					s.readRawBytes(buffer, thsize);
					QImage imth;
					imth.loadFromData((const uchar*)buffer, thsize, "JPEG");
					imth.convertDepth(32);
					if (resID == 0x0409)
						imgInfo.exifInfo.thumbnail = imth.swapRGB();
					else
						imgInfo.exifInfo.thumbnail = imth;
					imgInfo.exifDataValid = true;
					free(buffer);
				}
			default:
				break;
			}
		}
		s.device()->at( resBase + resSize );
		offset += resSize;
		if (resSize & 1)
		{
			s >> filler;
			offset += 1;
		}
	}
	if(offset<size)
		s.device()->at( size );
}

bool ScImage::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 layerName, 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;
			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;
			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()->at( s.device()->at() + layerRange );
			lay.layerName = getLayerString(s);
			imgInfo.layerInfo.append(lay);
			s >> signature;
			if( signature == 0x3842494D )
			{
				while (signature == 0x3842494D )
				{
					s >> signature;
					s >> addRes;
					s.device()->at( s.device()->at() + addRes );
					s >> signature;
				}
				s.device()->at( s.device()->at() - 4 );
			}
			else
			{
				s.device()->at( s.device()->at() - 2 );
				s >> signature;
				if( signature == 0x3842494D )
				{
					while (signature == 0x3842494D )
					{
						s >> signature;
						s >> addRes;
						s.device()->at( s.device()->at() + addRes );
						s >> signature;
					}
					s.device()->at( s.device()->at() - 4 );
				}
				else
					s.device()->at( s.device()->at() - 6 );
			}
		}
		bool firstLayer = true;
		for (int layer = 0; layer < numLayers; layer++)
		{
			loadLayerChannels( s, header, imgInfo.layerInfo, layer, &firstLayer );
		}
	}
	else
	{
		s >> numLayers;
		loadLayer( s, header);
	}
	return true;
}

// Load the PSD image.
bool ScImage::LoadPSD( QDataStream & s, const PSDHeader & header)
{
	// Create dst image.
	if( !create( header.width, header.height, 32 ))
		return false;
	setAlphaBuffer( true );
	uint tmp;
	uint cdataStart;
	uint ressourceDataLen;
	uint startRessource;
	uint layerDataLen;
	uint startLayers;

	srand(314159265);
	for (int i = 0; i < 4096; i++)
		random_table[i] = rand();
	for (int i = 0; i < 4096; i++)
	{
		int tmp;
		int swap = i + rand() % (4096 - i);
		tmp = random_table[i];
		random_table[i] = random_table[swap];
		random_table[swap] = tmp;
	}

	// Skip mode data. FIX: this is incorrect, it's the Colormap Data for indexed Images
	s >> tmp;
	cdataStart = s.device()->at();
	if (tmp != 0)
	{
		QValueList<uchar> colorTableR;
		QValueList<uchar> colorTableG;
		QValueList<uchar> colorTableB;
		colorTableR.clear();
		colorTableG.clear();
		colorTableB.clear();
		colorTable.clear();
		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++)
		{
			colorTable.append(qRgb(colorTableR[cc], colorTableG[cc], colorTableB[cc]));
		}
	}
	s.device()->at( cdataStart + tmp );
	s >> ressourceDataLen;
	startRessource = s.device()->at();
	if (ressourceDataLen != 0)
		parseRessourceData(s, header, ressourceDataLen);
	if  ((!imgInfo.exifInfo.thumbnail.isNull()) && (header.reserved[0] == 't'))
		return true;
	s.device()->at( startRessource + ressourceDataLen );
	// Skip the reserved data. FIX: Also incorrect, this is the actual Layer Data for Images with Layers
	s >> layerDataLen;
	startLayers = s.device()->at();
	if (layerDataLen != 0)
		return parseLayer( s, header);
	else
	{
		// Decoding simple psd file, no layers
		s.device()->at( s.device()->at() + layerDataLen );
		loadLayer( s, header);
	}
	return true;
}

void ScImage::scaleImage(int nwidth, int nheight)
{
	QImage dst;
	dst.create(nwidth, nheight,32);
	QRgb* xelrow = 0;
	QRgb* tempxelrow = 0;
	register QRgb* xP;
	register QRgb* nxP;
	int rows, cols, rowsread, newrows, newcols;
	register int row, col, needtoreadrow;
	const uchar maxval = 255;
	double xscale, yscale;
	long sxscale, syscale;
	register long fracrowtofill, fracrowleft;
	long* as;
	long* rs;
	long* gs;
	long* bs;
	int rowswritten = 0;
	cols = width();
	rows = height();
	newcols = dst.width();
	newrows = dst.height();
	long SCALE;
	long HALFSCALE;
	if (cols > 4096)
	{
		SCALE = 4096;
		HALFSCALE = 2048;
	}
	else
	{
		int fac = 4096;
		while (cols * fac > 4096)
		{
			fac /= 2;
		}
		SCALE = fac * cols;
		HALFSCALE = fac * cols / 2;
	}
	xscale = (double) newcols / (double) cols;
	yscale = (double) newrows / (double) rows;
	sxscale = (long)(xscale * SCALE);
	syscale = (long)(yscale * SCALE);
	if ( newrows != rows )	/* shortcut Y scaling if possible */
		tempxelrow = new QRgb[cols];
	as = new long[cols];
	rs = new long[cols];
	gs = new long[cols];
	bs = new long[cols];
	rowsread = 0;
	fracrowleft = syscale;
	needtoreadrow = 1;
	for ( col = 0; col < cols; ++col )
		rs[col] = gs[col] =  as[col] = bs[col] = HALFSCALE;
	fracrowtofill = SCALE;
	for ( row = 0; row < newrows; ++row )
	{
		if ( newrows == rows )
			tempxelrow = xelrow = (QRgb*)scanLine(rowsread++);
		else
		{
			while ( fracrowleft < fracrowtofill )
			{
				if ( needtoreadrow && rowsread < rows )
					xelrow = (QRgb*)scanLine(rowsread++);
				for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
				{
					as[col] += fracrowleft * qAlpha( *xP );
					rs[col] += fracrowleft * qRed( *xP );
					gs[col] += fracrowleft * qGreen( *xP );
					bs[col] += fracrowleft * qBlue( *xP );
				}
				fracrowtofill -= fracrowleft;
				fracrowleft = syscale;
				needtoreadrow = 1;
			}
			if ( needtoreadrow && rowsread < rows )
			{
				xelrow = (QRgb*)scanLine(rowsread++);
				needtoreadrow = 0;
			}
			register long a=0;
			for ( col = 0, xP = xelrow, nxP = tempxelrow; col < cols; ++col, ++xP, ++nxP )
			{
				register long r, g, b;
				a = as[col] + fracrowtofill * qAlpha( *xP );
				r = rs[col] + fracrowtofill * qRed( *xP );
				g = gs[col] + fracrowtofill * qGreen( *xP );
				b = bs[col] + fracrowtofill * qBlue( *xP );
				r /= SCALE;
				if ( r > maxval ) r = maxval;
				g /= SCALE;
				if ( g > maxval ) g = maxval;
				b /= SCALE;
				if ( b > maxval ) b = maxval;
				a /= SCALE;
				if ( a > maxval ) a = maxval;
				*nxP = qRgba( (int)r, (int)g, (int)b , (int)a);
				rs[col] = as[col] = gs[col] = bs[col] = HALFSCALE;
			}
			fracrowleft -= fracrowtofill;
			if ( fracrowleft == 0 )
			{
				fracrowleft = syscale;
				needtoreadrow = 1;
			}
			fracrowtofill = SCALE;
		}
		if ( newcols == cols )
			memcpy(dst.scanLine(rowswritten++), tempxelrow, newcols*4);
		else
		{
			register long a, r, g, b;
			register long fraccoltofill, fraccolleft = 0;
			register int needcol;
			nxP = (QRgb*)dst.scanLine(rowswritten++);
			fraccoltofill = SCALE;
			a = r = g = b = HALFSCALE;
			needcol = 0;
			for ( col = 0, xP = tempxelrow; col < cols; ++col, ++xP )
			{
				fraccolleft = sxscale;
				while ( fraccolleft >= fraccoltofill )
				{
					if ( needcol )
					{
						++nxP;
						a = r = g = b = HALFSCALE;
					}
					a += fraccoltofill * qAlpha( *xP );
					r += fraccoltofill * qRed( *xP );
					g += fraccoltofill * qGreen( *xP );
					b += fraccoltofill * qBlue( *xP );
					r /= SCALE;
					if ( r > maxval ) r = maxval;
					g /= SCALE;
					if ( g > maxval ) g = maxval;
					b /= SCALE;
					if ( b > maxval ) b = maxval;
					a /= SCALE;
					if ( a > maxval ) a = maxval;
					*nxP = qRgba( (int)r, (int)g, (int)b, (int)a );
					fraccolleft -= fraccoltofill;
					fraccoltofill = SCALE;
					needcol = 1;
				}
				if ( fraccolleft > 0 )
				{
					if ( needcol )
					{
						++nxP;
						a = r = g = b = HALFSCALE;
						needcol = 0;
					}
					a += fraccolleft * qAlpha( *xP );
					r += fraccolleft * qRed( *xP );
					g += fraccolleft * qGreen( *xP );
					b += fraccolleft * qBlue( *xP );
					fraccoltofill -= fraccolleft;
				}
			}
			if ( fraccoltofill > 0 )
			{
				--xP;
				a += fraccolleft * qAlpha( *xP );
				r += fraccoltofill * qRed( *xP );
				g += fraccoltofill * qGreen( *xP );
				b += fraccoltofill * qBlue( *xP );
			}
			if ( ! needcol )
			{
				r /= SCALE;
				if ( r > maxval ) r = maxval;
				g /= SCALE;
				if ( g > maxval ) g = maxval;
				b /= SCALE;
				if ( b > maxval ) b = maxval;
				a /= SCALE;
				if ( a > maxval ) a = maxval;
				*nxP = qRgba( (int)r, (int)g, (int)b, (int)a );
			}
		}
	}
	if ( newrows != rows && tempxelrow )// Robust, tempxelrow might be 0 1 day
		delete [] tempxelrow;
	if ( as )				// Avoid purify complaint
		delete [] as;
	if ( rs )				// Robust, rs might be 0 one day
		delete [] rs;
	if ( gs )				// Robust, gs might be 0 one day
		delete [] gs;
	if ( bs )				// Robust, bs might be 0 one day
		delete [] bs;
	create(nwidth, nheight,32);
	for( int yi=0; yi < dst.height(); ++yi )
	{
		QRgb *s = (QRgb*)(dst.scanLine( yi ));
		QRgb *d = (QRgb*)(scanLine( yi ));
		for(int xi=0; xi < dst.width(); ++xi )
		{
			(*d) = (*s);
			s++;
			d++;
		}
	}
	return;
}

QString ScImage::getAlpha(QString fn, bool PDF, bool pdf14, int gsRes, int scaleXSize, int scaleYSize)
{
	QString retS = "";
	float xres, yres;
	short resolutionunit = 0;
	imgInfo.valid = false;
	imgInfo.clipPath = "";
	imgInfo.PDSpathData.clear();
	imgInfo.layerInfo.clear();
	QFileInfo fi = QFileInfo(fn);
	if (!fi.exists())
		return retS;
	QString tmp, BBox, tmp2;
	QString ext = fi.extension(false).lower();
	QString tmpFile = QDir::convertSeparators(QDir::homeDirPath()+"/.scribus/sc.png");
	QString picFile = QDir::convertSeparators(fn);
	if ((ext == "jpg") || (ext == "jpeg"))
		return retS;
	double x, y, b, h;
	bool found = false;
	int retg = -1;
	QChar tc;
	if (ext == "pdf")
	{
		QStringList args;
		xres = gsRes;
		yres = gsRes;
		args.append("-r"+QString::number(gsRes));
//		args.append("-sOutputFile=\""+tmpFile + "\"");
		args.append("-sOutputFile="+tmpFile);
		args.append("-dFirstPage=1");
		args.append("-dLastPage=1");
//		args.append("\""+picFile+"\"");
		args.append(picFile);
		retg = callGS(args);
		if (retg == 0)
		{
			load(tmpFile);
			unlink(tmpFile);
			setAlphaBuffer(true);
			if (ScMW->HavePngAlpha != 0)
			{
				for( int yi=0; yi < height(); ++yi )
				{
					QRgb *s = (QRgb*)(scanLine( yi ));
					for(int xi=0; xi < width(); ++xi )
					{
						if((*s) == 0xffffffff)
							(*s) &= 0x00ffffff;
						s++;
					}
				}
			}
		}
	}
	else if ((ext == "eps") || (ext == "ps"))
	{
		QFile f(fn);
		if (f.open(IO_ReadOnly))
		{
			QTextStream ts(&f);
			while (!ts.atEnd())
			{
				tc = ' ';
				tmp = "";
				while ((tc != '\n') && (tc != '\r'))
				{
					ts >> tc;
					if ((tc != '\n') && (tc != '\r'))
						tmp += tc;
				}
				if (tmp.startsWith("%%BoundingBox:"))
				{
					found = true;
					BBox = tmp.remove("%%BoundingBox:");
					if (BBox.contains("(atend)"))
						found = false;
				}
				if (!found)
				{
					if (tmp.startsWith("%%BoundingBox"))
					{
						found = true;
						BBox = tmp.remove("%%BoundingBox");
						if (BBox.contains("(atend)"))
							found = false;
					}
				}
				if ((tmp.startsWith("%%EndComments")) && (found))
					break;
			}
		}
		f.close();
		if (found)
		{
			QTextStream ts2(&BBox, IO_ReadOnly);
			ts2 >> x >> y >> b >> h;
			h = h * gsRes / 72.0;
			QStringList args;
			xres = gsRes;
			yres = gsRes;
			if (ext == "eps")
				args.append("-dEPSCrop");
			args.append("-r"+QString::number(gsRes));
			args.append("-sOutputFile="+tmpFile);
			args.append(picFile);
			retg = callGS(args);
			if (retg == 0)
			{
				QImage image;
				image.load(tmpFile);
				image.setAlphaBuffer(true);
				if (ScMW->HavePngAlpha != 0)
				{
					int wi = image.width();
					int hi = image.height();
					for( int yi=0; yi < hi; ++yi )
					{
						QRgb *s = (QRgb*)(image.scanLine( yi ));
						QRgb alphaFF = qRgba(255,255,255,255);
						QRgb alpha00 = qRgba(255,255,255,  0);
						for(int xi=0; xi < wi; ++xi )
						{
							if((*s) == alphaFF)
								(*s) &= alpha00;
							s++;
						}
					}
				}
				if (ext == "eps")
				{
					double xc = x * gsRes / 72.0;
					y = y * gsRes / 72.0;
					b = b * gsRes / 72.0;
					if (((b - xc) < image.width()) || ((h - y) < image.height()))
					{
						int yc = qRound(image.height() - y - (h-y));
						*this = static_cast<ScImage>(image.copy(qRound(xc), yc, qRound(b - x), qRound(h - y)));
					}
					else
						*this = static_cast<ScImage>(image.copy());
				}
				else
					*this = static_cast<ScImage>(image.copy());
				setAlphaBuffer(true);
				unlink(tmpFile);
			}
		}
	}
	else if ((ext == "tif") || (ext == "tiff"))
	{
#ifdef HAVE_TIFF
		TIFF* tif = TIFFOpen(fn.local8Bit(), "r");
		if(tif)
		{
			unsigned width, height, size;
			TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
			TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
			TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres);
			TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres);
			TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT , &resolutionunit);
			size = width * height;
			uint16 photometric, bitspersample, samplesperpixel, fillorder;
			TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric);
			TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitspersample);
			TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel);
			TIFFGetField(tif, TIFFTAG_FILLORDER, &fillorder);
			uint32 *bits = 0;
			if (photometric == PHOTOMETRIC_SEPARATED)
			{
				TIFFClose(tif);
				return retS;
			}
			else
			{
				create(width,height,32);
				setAlphaBuffer(true);
				bits = (uint32 *) _TIFFmalloc(size * sizeof(uint32));
				if(bits)
				{
					if (TIFFReadRGBAImage(tif, width, height, bits, 0))
					{
						for(unsigned int y = 0; y < height; y++)
							memcpy(scanLine(height - 1 - y), bits + y * width, width * 4);
					}
					_TIFFfree(bits);
				}
				TIFFClose(tif);
			}
		}
#else
		qDebug("TIFF Support not available");
#endif // HAVE_TIFF

	}
	else if (ext == "psd")
	{
		QFile f(fn);
		if (f.open(IO_ReadOnly))
		{
			xres = 72.0;
			yres = 72.0;
			QDataStream s( &f );
			s.setByteOrder( QDataStream::BigEndian );
			PSDHeader header;
			s >> header;
			// Check image file format.
			if( s.atEnd() || !IsValid( header ) )
				return retS;
			// Check if it's a supported format.
			if( !IsSupported( header ) )
				return retS;
			if (header.color_mode == CM_CMYK)
				return retS;
			if( !LoadPSD(s, header) )
				return retS;
			f.close();
		}
		else
			return retS;
	}
	else
	{
		if (load(fn))
		{
			*this = convertDepth(32);
 			 if (hasAlphaBuffer())
				setAlphaBuffer(true);
			else
				return retS;
		}
		else
			return retS;
	}
	if (isNull())
		return retS;
	if ((scaleXSize != 0) && (scaleYSize != 0))
		*this = smoothScale(scaleXSize, scaleYSize);
	int i = 0;
	unsigned char u;
	int hm = height();
	int wm = width();
	int w2;
	if (pdf14)
	{
		retS.reserve(hm * wm);
		for( int yi=0; yi < hm; ++yi )
		{
			QRgb * s = (QRgb*)(scanLine( yi ));
			for( int xi=0; xi < wm; ++xi )
			{
				QRgb r=*s++;
				u=qAlpha(r);
				retS[i++] = u;
			}
		}
	}
	else
	{
		QImage iMask = createAlphaMask();
		hm = iMask.height();
		wm = iMask.width();
		w2 = wm / 8;
		if ((wm % 8) != 0)
			w2++;
		retS.reserve(hm * w2);
		for( int yi=0; yi < hm; ++yi )
		{
			uchar * s = iMask.scanLine( yi );
			for( int xi=0; xi < w2; ++xi )
			{
				u = *(s+xi);
				if(PDF) u = ~u;
				retS[i++] = u;
			}
		}
	}
	return retS;
}

#define ICC_MARKER  (JPEG_APP0 + 2)	/* JPEG marker code for ICC */
#define PHOTOSHOP_MARKER  (JPEG_APP0 + 13)	/* JPEG marker code for PHOTOSHOP */
#define ICC_OVERHEAD_LEN  14		/* size of non-profile data in APP2 */
#define MAX_BYTES_IN_MARKER  65533	/* maximum data len of a JPEG marker */
#define MAX_DATA_BYTES_IN_MARKER  (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN)

bool ScImage::marker_is_icc (jpeg_saved_marker_ptr marker)
{
	return
	    marker->marker == ICC_MARKER &&
	    marker->data_length >= ICC_OVERHEAD_LEN &&
	    /* verify the identifying string */
	    GETJOCTET(marker->data[0]) == 0x49 &&
	    GETJOCTET(marker->data[1]) == 0x43 &&
	    GETJOCTET(marker->data[2]) == 0x43 &&
	    GETJOCTET(marker->data[3]) == 0x5F &&
	    GETJOCTET(marker->data[4]) == 0x50 &&
	    GETJOCTET(marker->data[5]) == 0x52 &&
	    GETJOCTET(marker->data[6]) == 0x4F &&
	    GETJOCTET(marker->data[7]) == 0x46 &&
	    GETJOCTET(marker->data[8]) == 0x49 &&
	    GETJOCTET(marker->data[9]) == 0x4C &&
	    GETJOCTET(marker->data[10]) == 0x45 &&
	    GETJOCTET(marker->data[11]) == 0x0;
}

bool ScImage::marker_is_photoshop (jpeg_saved_marker_ptr marker)
{
	return
	    marker->marker == PHOTOSHOP_MARKER &&
	    marker->data_length >= ICC_OVERHEAD_LEN &&
	    /* verify the identifying string */
	    GETJOCTET(marker->data[0]) == 0x50 &&
	    GETJOCTET(marker->data[1]) == 0x68 &&
	    GETJOCTET(marker->data[2]) == 0x6F &&
	    GETJOCTET(marker->data[3]) == 0x74 &&
	    GETJOCTET(marker->data[4]) == 0x6F &&
	    GETJOCTET(marker->data[5]) == 0x73 &&
	    GETJOCTET(marker->data[6]) == 0x68 &&
	    GETJOCTET(marker->data[7]) == 0x6F &&
	    GETJOCTET(marker->data[8]) == 0x70 &&
	    GETJOCTET(marker->data[9]) == 0x20 &&
	    GETJOCTET(marker->data[10]) == 0x33 &&
	    GETJOCTET(marker->data[11]) == 0x2E &&
	    GETJOCTET(marker->data[12]) == 0x30 &&
	    GETJOCTET(marker->data[13]) == 0x0;
}
/* Small modification of original read_icc_profile method from jpegicc of lcms project
 * to enable read of Photoshop marker
 */
bool ScImage::read_jpeg_marker (UINT8 requestmarker, j_decompress_ptr cinfo, JOCTET **icc_data_ptr, unsigned int *icc_data_len)
{
	jpeg_saved_marker_ptr marker;
	int num_markers = 0;
	int seq_no;
	JOCTET *icc_data;
	unsigned int total_length;
#define MAX_SEQ_NO  255		/* sufficient since marker numbers are bytes */
	char marker_present[MAX_SEQ_NO+1];	  /* 1 if marker found */
	unsigned int data_length[MAX_SEQ_NO+1]; /* size of profile data in marker */
	unsigned int data_offset[MAX_SEQ_NO+1]; /* offset for data in marker */

	*icc_data_ptr = NULL;		/* avoid confusion if false return */
	*icc_data_len = 0;

	/* This first pass over the saved markers discovers whether there are
	 * any ICC markers and verifies the consistency of the marker numbering.
	 */

	for (seq_no = 1; seq_no <= MAX_SEQ_NO; seq_no++)
		marker_present[seq_no] = 0;
	seq_no = 0;
	for (marker = cinfo->marker_list; marker != NULL; marker = marker->next)
	{
		if (requestmarker == ICC_MARKER && marker_is_icc(marker))
		{
			if (num_markers == 0)
				num_markers = GETJOCTET(marker->data[13]);
			else if (num_markers != GETJOCTET(marker->data[13]))
				return false;		/* inconsistent num_markers fields */
			seq_no = GETJOCTET(marker->data[12]);
			if (seq_no <= 0 || seq_no > num_markers)
				return false;		/* bogus sequence number */
			if (marker_present[seq_no])
				return false;		/* duplicate sequence numbers */
			marker_present[seq_no] = 1;
			data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN;
		}
		else if(requestmarker == PHOTOSHOP_MARKER && marker_is_photoshop(marker))
		{
			num_markers = ++seq_no;
			marker_present[seq_no] = 1;
			data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN;
		}
	}

	if (num_markers == 0)
		return false;

	/* Check for missing markers, count total space needed,
	 * compute offset of each marker's part of the data.
	 */

	total_length = 0;
	for (seq_no = 1; seq_no <= num_markers; seq_no++)
	{
		if (marker_present[seq_no] == 0)
			return false;		/* missing sequence number */
		data_offset[seq_no] = total_length;
		total_length += data_length[seq_no];
	}

	if (total_length <= 0)
		return false;		/* found only empty markers? */

	/* Allocate space for assembled data */
	icc_data = (JOCTET *) malloc(total_length * sizeof(JOCTET));
	if (icc_data == NULL)
		return false;		/* oops, out of memory */
	seq_no=0;
	/* and fill it in */
	for (marker = cinfo->marker_list; marker != NULL; marker = marker->next)
	{
		if ( (requestmarker == ICC_MARKER && marker_is_icc(marker)) ||
		        (requestmarker == PHOTOSHOP_MARKER && marker_is_photoshop(marker)) || (requestmarker == 0xE1))
		{
			JOCTET FAR *src_ptr;
			JOCTET *dst_ptr;
			unsigned int length;
			if(requestmarker == ICC_MARKER)
				seq_no = GETJOCTET(marker->data[12]);
			else if(requestmarker == PHOTOSHOP_MARKER)
				seq_no++;
			dst_ptr = icc_data + data_offset[seq_no];
			src_ptr = marker->data + ICC_OVERHEAD_LEN;
			length = data_length[seq_no];
			while (length--)
			{
				*dst_ptr++ = *src_ptr++;
			}
		}
	}

	*icc_data_ptr = icc_data;
	*icc_data_len = total_length;

	return true;
}

void ScImage::getEmbeddedProfile(const QString & fn, QString *profile, int *components)
{
	Q_ASSERT(profile);
	Q_ASSERT(components);
#ifdef HAVE_CMS
	cmsHPROFILE tiffProf = 0;
	QFileInfo fi = QFileInfo(fn);
	if (!fi.exists())
		return;
	QString ext = fi.extension(false).lower();
	iccbuf = 0;
	icclen = 0;
	if (ext == "psd")
	{
		QFile f(fn);
		if (f.open(IO_ReadOnly))
		{
			imgInfo.xres = 72;
			imgInfo.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( !LoadPSD(s, header) )
				return;
			if (icclen > 0)
			{
				tiffProf = cmsOpenProfileFromMem(iccbuf, icclen);
				if (tiffProf)
				{
					if (static_cast<int>(cmsGetColorSpace(tiffProf)) == icSigRgbData)
						*components = 3;
					if (static_cast<int>(cmsGetColorSpace(tiffProf)) == icSigCmykData)
						*components = 4;
					for (uint el = 0; el < icclen; ++el)
						*profile += iccbuf[el];
				}
				cmsCloseProfile(tiffProf);
				free(iccbuf);
			}
			f.close();
		}
		else
			return;
	}
#ifdef HAVE_TIFF
	else if ((ext == "tif") || (ext == "tiff"))
	{
		TIFF* tif = TIFFOpen(fn.local8Bit(), "r");
		if(tif)
		{
			DWORD EmbedLen = 0;
			LPBYTE EmbedBuffer;
			if (TIFFGetField(tif, TIFFTAG_ICCPROFILE, &EmbedLen, &EmbedBuffer))
			{
				tiffProf = cmsOpenProfileFromMem(EmbedBuffer, EmbedLen);
				if (tiffProf)
				{
					if (static_cast<int>(cmsGetColorSpace(tiffProf)) == icSigRgbData)
						*components = 3;
					if (static_cast<int>(cmsGetColorSpace(tiffProf)) == icSigCmykData)
						*components = 4;
					for (uint el = 0; el < EmbedLen; ++el)
						*profile += EmbedBuffer[el];
				}
				cmsCloseProfile(tiffProf);
			}
			TIFFClose(tif);
		}
	}
#endif // HAVE_TIFF
	else if ((ext == "jpg") || (ext == "jpeg"))
	{
		struct jpeg_decompress_struct cinfo;
		struct my_error_mgr         jerr;
		FILE     *infile;
		cinfo.err = jpeg_std_error (&jerr.pub);
		jerr.pub.error_exit = my_error_exit;
		infile = NULL;
		if (setjmp (jerr.setjmp_buffer))
		{
			jpeg_destroy_decompress (&cinfo);
			if (infile)
				fclose (infile);
			return;
		}
		jpeg_create_decompress (&cinfo);
		if ((infile = fopen (fn.local8Bit(), "rb")) == NULL)
			return;
		jpeg_stdio_src(&cinfo, infile);
		jpeg_save_markers(&cinfo, ICC_MARKER, 0xFFFF);
		jpeg_read_header(&cinfo, true);
		//jpeg_start_decompress(&cinfo);
		unsigned int EmbedLen = 0;
		unsigned char* EmbedBuffer;
		if (read_jpeg_marker(ICC_MARKER,&cinfo, &EmbedBuffer, &EmbedLen))
		{
			tiffProf = cmsOpenProfileFromMem(EmbedBuffer, EmbedLen);
			if (tiffProf)
			{
				if (static_cast<int>(cmsGetColorSpace(tiffProf)) == icSigRgbData)
					*components = 3;
				if (static_cast<int>(cmsGetColorSpace(tiffProf)) == icSigCmykData)
					*components = 4;
				for (uint el = 0; el < EmbedLen; ++el)
					*profile += EmbedBuffer[el];
			}
			cmsCloseProfile(tiffProf);
			free(EmbedBuffer);
		}
		//(void) jpeg_finish_decompress(&cinfo);
		fclose (infile);
		jpeg_destroy_decompress (&cinfo);
	}
	else
		return;
#endif // HAVE_CMS
}

bool ScImage::LoadPicture(const QString & fn, const QString & Prof,
						  int rend, bool useEmbedded, bool useProf,
						  int requestType, int gsRes, bool *realCMYK, 
						  bool showMsg)
{
	// requestType - 0: CMYK, 1: RGB, 2: RGB Proof 3 : RawData, 4: Thumbnail
	// gsRes - is the resolution that ghostscript will render at
	bool isCMYK = false;
	bool ret = false;
	if (realCMYK != 0)
		*realCMYK = false;
	bool bilevel = false;
	float xres, yres;
	short resolutionunit = 0;
	bool systemBigEndian;
	int systemWordsize;
	qSysInfo( &systemWordsize, &systemBigEndian);
	int reqType = requestType;
#ifdef HAVE_CMS
	cmsHTRANSFORM xform = 0;
	cmsHPROFILE inputProf = 0;
	cmsHPROFILE tiffProf = 0;
	int cmsFlags = 0;
	int cmsProofFlags = 0;
#endif
	QFileInfo fi = QFileInfo(fn);
	if (!fi.exists())
		return ret;
	QString ext = fi.extension(false).lower();
	QString tmp, dummy, cmd1, cmd2, BBox, tmp2;
	QChar tc;
	double x, y, b, h;
	bool found = false;
	int retg = -1;
	QString message;
	QString tmpFile = QDir::convertSeparators(QDir::homeDirPath()+"/.scribus/sc.png");
	QString picFile = QDir::convertSeparators(fn);
	if (ext == "pdf")
	{
		imgInfo.typ = 4;
		imgInfo.exifDataValid = false;
		if (reqType == 4)
			reqType = 1;
		QStringList args;
		xres = gsRes;
		yres = gsRes;
		args.append("-r"+QString::number(gsRes));
		args.append("-sOutputFile="+tmpFile);
		args.append("-dFirstPage=1");
		args.append("-dLastPage=1");
		args.append(picFile);
		retg = callGS(args);
		if (retg == 0)
		{
			QImage image;
			load(tmpFile);
			unlink(tmpFile);
			setAlphaBuffer(true);
			if (ScMW->HavePngAlpha != 0)
			{
				for( int yi=0; yi < height(); ++yi )
				{
					QRgb *s = (QRgb*)(scanLine( yi ));
					for(int xi=0; xi < width(); ++xi )
					{
						if((*s) == 0xffffffff)
							(*s) &= 0x00ffffff;
						s++;
					}
				}
			}
			imgInfo.BBoxX = 0;
			imgInfo.BBoxH = height();
			imgInfo.xres = qRound(gsRes);
			imgInfo.yres = qRound(gsRes);
			imgInfo.colorspace = 0;
			setDotsPerMeterX ((int) (xres / 0.0254));
			setDotsPerMeterY ((int) (yres / 0.0254));
		}
	}
	if ((ext == "eps") || (ext == "ps"))
	{
		imgInfo.typ = 3;
		imgInfo.exifDataValid = false;
		if (reqType == 4)
			reqType = 1;
		QFile f(fn);
		if (f.open(IO_ReadOnly))
		{
			QTextStream ts(&f);
			while (!ts.atEnd())
			{
				tc = ' ';
				tmp = "";
				while ((tc != '\n') && (tc != '\r'))
				{
					ts >> tc;
					if ((tc != '\n') && (tc != '\r'))
						tmp += tc;
				}
				if (tmp.startsWith("%%BoundingBox:"))
				{
					found = true;
					BBox = tmp.remove("%%BoundingBox:");
					if (BBox.contains("(atend)"))
						found = false;
				}
				if (!found)
				{
					if (tmp.startsWith("%%BoundingBox"))
					{
						found = true;
						BBox = tmp.remove("%%BoundingBox");
						if (BBox.contains("(atend)"))
							found = false;
					}
				}
				if ((tmp.startsWith("%%EndComments")) && (found))
					break;
			}
		}
		f.close();
		if (found)
		{
			QTextStream ts2(&BBox, IO_ReadOnly);
			ts2 >> x >> y >> b >> h;
/*			x = x * gsRes / 72.0;
			y = y * gsRes / 72.0;
			b = b * gsRes / 72.0; */
			h = h * gsRes / 72.0;
			QStringList args;
			xres = gsRes;
			yres = gsRes;
			if (ext == "eps")
				args.append("-dEPSCrop");
			args.append("-r"+QString::number(gsRes));
			args.append("-sOutputFile="+tmpFile);
//			args.append("-g"+tmp.setNum(qRound(b))+"x"+tmp2.setNum(qRound(h)));
			args.append(picFile);
			retg = callGS(args);
			if (retg == 0)
			{
				QImage image;
				image.load(tmpFile);
				image.setAlphaBuffer(true);
				if (ScMW->HavePngAlpha != 0)
				{
					int wi = image.width();
					int hi = image.height();
					for( int yi=0; yi < hi; ++yi )
					{
						QRgb *s = (QRgb*)(image.scanLine( yi ));
						QRgb alphaFF = qRgba(255,255,255,255);
						QRgb alpha00 = qRgba(255,255,255,  0);
						for(int xi=0; xi < wi; ++xi )
						{
							if((*s) == alphaFF)
								(*s) &= alpha00;
							s++;
						}
					}
				}
//				*this = static_cast<ScImage>(image.copy(static_cast<int>(x), 0, static_cast<int>(b-x), static_cast<int>(h-y)));
				unlink(tmpFile);
				if (ext == "eps")
				{
					double xc = x * gsRes / 72.0;
					y = y * gsRes / 72.0;
					b = b * gsRes / 72.0;
					if (((b - xc) < image.width()) || ((h - y) < image.height()))
					{
						int yc = qRound(image.height() - y - (h-y));
						*this = static_cast<ScImage>(image.copy(qRound(xc), yc, qRound(b - x), qRound(h - y)));
					}
					else
						*this = static_cast<ScImage>(image.copy());
					imgInfo.BBoxX = static_cast<int>(x);
					imgInfo.BBoxH = static_cast<int>(h);
				}
				else
				{
					*this = static_cast<ScImage>(image.copy());
					imgInfo.BBoxX = 0;
					imgInfo.BBoxH = height();
				}
				imgInfo.xres = qRound(gsRes);
				imgInfo.yres = qRound(gsRes);
				imgInfo.colorspace = 0;
				setDotsPerMeterX ((int) (xres / 0.0254));
				setDotsPerMeterY ((int) (yres / 0.0254));
			}
		}
	}
#ifdef HAVE_TIFF
	else if ((ext == "tif") || (ext == "tiff"))
	{
		imgInfo.typ = 1;
		if (reqType == 4)
			reqType = 1;
		QImage img2;
		TIFF* tif = TIFFOpen(fn.local8Bit(), "r");
		if(tif)
		{
			unsigned widtht, heightt, size;
			char *description=0, *copyright=0, *datetime=0, *artist=0, *scannerMake=0, *scannerModel=0;

			TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &widtht);
			TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &heightt);
			TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres);
			TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres);
			TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT , &resolutionunit);
			size = widtht * heightt;
			uint16 photometric, bitspersample, samplesperpixel, fillorder;
			TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric);
			TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitspersample);
			TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel);
			TIFFGetField(tif, TIFFTAG_FILLORDER, &fillorder);

			TIFFGetField(tif, TIFFTAG_MAKE, &scannerMake);
			TIFFGetField(tif, TIFFTAG_MODEL, &scannerModel);
			TIFFGetField(tif, TIFFTAG_IMAGEDESCRIPTION, &description);
			TIFFGetField(tif, TIFFTAG_COPYRIGHT, &copyright);
			TIFFGetField(tif, TIFFTAG_DATETIME, &datetime);
			TIFFGetField(tif, TIFFTAG_ARTIST, &artist);
			imgInfo.exifInfo.cameraName = QString(scannerModel);
			imgInfo.exifInfo.cameraVendor = QString(scannerMake);
			imgInfo.exifInfo.comment = QString(description);
			imgInfo.exifInfo.userComment = QString(copyright);
			imgInfo.exifInfo.width = widtht;
			imgInfo.exifInfo.height = heightt;
			imgInfo.exifInfo.dateTime = QString(datetime);
			imgInfo.exifInfo.artist = QString(artist);
			imgInfo.exifInfo.thumbnail = QImage();
			imgInfo.exifDataValid = true;

			if( xres <= 1.0 || yres <= 1.0 )
			{
				xres = yres = 72.0;
				QFileInfo qfi(fn);
				message = QObject::tr("%1 may be corrupted : missing resolution tags").arg(qfi.fileName());
			}

			if (!create(widtht,heightt,32))
			{
				TIFFClose(tif);
				return ret;
			}
			setAlphaBuffer(true);
			uint32 *bits = 0;
			if (photometric == PHOTOMETRIC_SEPARATED)
			{
				if (samplesperpixel > 4)  // we can't handle CMYKA yet
				{
					bits = (uint32 *) _TIFFmalloc(size * sizeof(uint32));
					if(bits)
					{
						if (TIFFReadRGBAImage(tif, widtht, heightt, bits, 0))
						{
							for(unsigned int y = 0; y < heightt; y++)
								memcpy(scanLine(heightt - 1 - y), bits + y * widtht, widtht * 4);
						}
						_TIFFfree(bits);
						if (bitspersample == 1)
							bilevel = true;
						isCMYK = false;
					}
					swapRGBA();
				}
				else
				{
					if (TIFFIsTiled(tif))
					{
						uint32 columns, rows;
						uint32 *tile_buf;
						uint32 xt, yt;
						TIFFGetField(tif, TIFFTAG_TILEWIDTH,  &columns);
						TIFFGetField(tif, TIFFTAG_TILELENGTH, &rows);
						tile_buf = (uint32*) _TIFFmalloc(columns*rows*sizeof(uint32));
						if (tile_buf == NULL)
						{
							TIFFClose(tif);
							return ret;
						}
						uint32 tileW = columns, tileH = rows;
						for (yt = 0; yt < (uint32)height(); yt += rows)
						{
							if (yt > (uint)height())
								break;
							if (height()-yt < rows)
								tileH = height()-yt;
							tileW = columns;
							register uint32 yi;
							for (xt = 0; xt < (uint)width(); xt += columns)
							{
								TIFFReadTile(tif, tile_buf, xt, yt, 0, 0);
								for (yi = 0; yi < tileH; yi++)
									_TIFFmemcpy(scanLine(yt+(tileH-1-yi))+xt, tile_buf+tileW*yi, tileW*4);
							}
						}
						_TIFFfree(tile_buf);
					}
					else
					{
						tsize_t bytesperrow = TIFFScanlineSize(tif);
						bits = (uint32 *) _TIFFmalloc(bytesperrow);
						if (bits)
						{
							for (unsigned int y = 0; y < heightt; y++)
							{
								if (TIFFReadScanline(tif, bits, y, 0))
								{
									memcpy(scanLine(y), bits, widtht * 4);
								}
							}
							_TIFFfree(bits);
						}
					}
					isCMYK = true;
					if (realCMYK != 0)
						*realCMYK = true;
					if (systemBigEndian) {
						swapByteOrder(3,0,1,2);
					}
					else {
						swapRGBA();
					}
				}
			}
			else
			{
				bits = (uint32 *) _TIFFmalloc(size * sizeof(uint32));
				if(bits)
				{
					if (TIFFReadRGBAImage(tif, widtht, heightt, bits, 0))
					{
						for(unsigned int y = 0; y < heightt; y++)
							memcpy(scanLine(heightt - 1 - y), bits + y * widtht, widtht * 4);
					}
					_TIFFfree(bits);
					if (bitspersample == 1)
						bilevel = true;
				}
				swapRGBA();
			}
#ifdef HAVE_CMS
			DWORD EmbedLen = 0;
			LPBYTE EmbedBuffer;
			if (TIFFGetField(tif, TIFFTAG_ICCPROFILE, &EmbedLen, &EmbedBuffer) && useEmbedded && CMSuse && useProf)
			{
				const char *Descriptor;
				tiffProf = cmsOpenProfileFromMem(EmbedBuffer, EmbedLen);
				Descriptor = cmsTakeProductDesc(tiffProf);
				imgInfo.profileName = QString(Descriptor);
				imgInfo.isEmbedded = true;
			}
			else
			{
				imgInfo.isEmbedded = false;
				imgInfo.profileName = "";
			}
#endif // HAVE_CMS
			unsigned int PhotoshopLen = 0;
			unsigned char* PhotoshopBuffer;
			if (TIFFGetField(tif, TIFFTAG_PHOTOSHOP, &PhotoshopLen, &PhotoshopBuffer) )
			{
				if (PhotoshopLen != 0)
				{
					QByteArray arrayPhot(PhotoshopLen);
					arrayPhot.duplicate((const char*)PhotoshopBuffer,PhotoshopLen);
					QDataStream strPhot(arrayPhot,IO_ReadOnly);
					strPhot.setByteOrder( QDataStream::BigEndian );
					PSDHeader fakeHeader;
					fakeHeader.width = width();
					fakeHeader.height = height();
					parseRessourceData(strPhot, fakeHeader, PhotoshopLen);
					imgInfo.valid = (imgInfo.PDSpathData.size())>0?true:false;
				}
			}
			//			uint32 ClipLen = 0;
			//			uint8 ClipBuffer;
			//			QString db;
			//			if (TIFFGetField(tif, TIFFTAG_CLIPPATH, &ClipLen, &ClipBuffer))
			//				qDebug("%s", db.setNum(ClipLen).ascii());
			TIFFClose(tif);
			if (resolutionunit == RESUNIT_INCH)
			{
				setDotsPerMeterX ((int) (xres / 0.0254));
				setDotsPerMeterY ((int) (yres / 0.0254));
				imgInfo.xres = qRound(xres);
				imgInfo.yres = qRound(yres);
			}
			else if (resolutionunit == RESUNIT_CENTIMETER)
			{
				setDotsPerMeterX ((int) (xres * 100.0));
				setDotsPerMeterY ((int) (yres * 100.0));
				imgInfo.xres = qRound(xres*2.54);
				imgInfo.yres = qRound(yres*2.54);
			}
			if (isCMYK)
				imgInfo.colorspace = 1;
			else if (bilevel)
				imgInfo.colorspace = 2;
			else
				imgInfo.colorspace = 0;
			imgInfo.layerInfo.clear();
			imgInfo.BBoxX = 0;
			imgInfo.BBoxH = height();
		}
	}
#endif // HAVE_TIFF
	else if (ext == "psd")
	{
		imgInfo.typ = 2;
		imgInfo.exifDataValid = false;
		QFile f(fn);
		if (f.open(IO_ReadOnly))
		{
			imgInfo.xres = 72;
			imgInfo.yres = 72;
			QDataStream s( &f );
			s.setByteOrder( QDataStream::BigEndian );
			PSDHeader header;
			s >> header;
			// Check image file format.
			if( s.atEnd() || !IsValid( header ) )
				return ret;
			// Check if it's a supported format.
			if( !IsSupported( header ) )
				return ret;
			iccbuf = 0;
			icclen = 0;
			if (reqType == 4)
				header.reserved[0] = 't';
			if( !LoadPSD(s, header) )
				return ret;
#ifdef HAVE_CMS
			if (icclen>0)
			{
				if (useEmbedded && CMSuse && useProf)
				{
					tiffProf = cmsOpenProfileFromMem(iccbuf, icclen);
					const char *Descriptor;
					Descriptor = cmsTakeProductDesc(tiffProf);
					imgInfo.profileName = QString(Descriptor);
					imgInfo.isEmbedded = true;
				}
				else
				{
					imgInfo.isEmbedded = false;
					imgInfo.profileName = "";
				}
				free(iccbuf);
			}
#endif // HAVE_CMS
			if (header.color_mode == CM_CMYK)
				isCMYK = true;
			else
				isCMYK = false;
			if (realCMYK != 0)
				*realCMYK = isCMYK;
			if (header.color_mode == CM_CMYK)
				imgInfo.colorspace = 1;
			else if (header.color_mode == CM_RGB)
				imgInfo.colorspace = 0;
			else if (header.color_mode == CM_GRAYSCALE)
				imgInfo.colorspace = 2;
			imgInfo.valid = true;
			setDotsPerMeterX ((int) (imgInfo.xres / 0.0254));
			setDotsPerMeterY ((int) (imgInfo.yres / 0.0254));
			xres = imgInfo.xres;
			yres = imgInfo.yres;
			f.close();
			if (reqType == 4)
			{
				if (!imgInfo.exifInfo.thumbnail.isNull())
				{
					create(imgInfo.exifInfo.thumbnail.width(), imgInfo.exifInfo.thumbnail.height(), 32);
					for( int yit=0; yit < imgInfo.exifInfo.thumbnail.height(); ++yit )
					{
						QRgb *s = (QRgb*)(imgInfo.exifInfo.thumbnail.scanLine( yit ));
						QRgb *d = (QRgb*)(scanLine( yit ));
						for(int xit=0; xit < imgInfo.exifInfo.thumbnail.width(); ++xit )
						{
							(*d) = (*s);
							s++;
							d++;
						}
					}
					imgInfo.exifInfo.width = header.width;
					imgInfo.exifInfo.height = header.height;
					return true;
				}
				else
					reqType = 1;
			}
			imgInfo.exifInfo.width = header.width;
			imgInfo.exifInfo.height = header.height;
			imgInfo.BBoxX = 0;
			imgInfo.BBoxH = height();
		}
		else
			return ret;
	}
	else if ((ext == "jpg") || (ext == "jpeg"))
	{
		bool fromPS = false;
		imgInfo.typ = 0;
		ExifData ExifInf;
		imgInfo.exifInfo.thumbnail = QImage();
		struct jpeg_decompress_struct cinfo;
		struct my_error_mgr         jerr;
		FILE     *infile;
		cinfo.err = jpeg_std_error (&jerr.pub);
		jerr.pub.error_exit = my_error_exit;
		infile = NULL;
		if (setjmp (jerr.setjmp_buffer))
		{
			jpeg_destroy_decompress (&cinfo);
			if (infile)
				fclose (infile);
			return ret;
		}
		jpeg_create_decompress (&cinfo);
		if ((infile = fopen (fn.local8Bit(), "rb")) == NULL)
			return ret;
		jpeg_stdio_src(&cinfo, infile);
		jpeg_save_markers(&cinfo, ICC_MARKER, 0xFFFF);
		jpeg_save_markers(&cinfo, PHOTOSHOP_MARKER, 0xFFFF);
		jpeg_read_header(&cinfo, true);
		jpeg_start_decompress(&cinfo);
		bool exi = ExifInf.scan(fn);
		if ((exi) && (ExifInf.exifDataValid))
		{
			if ((!ExifInf.isNullThumbnail()) && (reqType == 4))
				*this = ExifInf.getThumbnail();
			imgInfo.exifInfo.cameraName = ExifInf.getCameraModel();
			imgInfo.exifInfo.cameraVendor = ExifInf.getCameraMake();
			imgInfo.exifInfo.thumbnail = ExifInf.getThumbnail();
			imgInfo.exifInfo.comment = ExifInf.getComment();
			imgInfo.exifInfo.width = ExifInf.getWidth();
			imgInfo.exifInfo.height = ExifInf.getHeight();
			imgInfo.exifInfo.userComment = ExifInf.getUserComment();
			imgInfo.exifInfo.dateTime = ExifInf.getDateTime();
			imgInfo.exifDataValid = true;
			if (cinfo.density_unit == 0)
			{
				xres = 72;
				yres = 72;
			}
			else if ( cinfo.density_unit == 1 )
			{
				xres = cinfo.X_density;
				yres = cinfo.Y_density;
			}
			else if ( cinfo.density_unit == 2 )
			{
				xres = cinfo.X_density * 2.54;
				yres = cinfo.Y_density * 2.54;
			}
			if( xres <= 1.0 || yres <= 1.0 || xres > 3000.0 || yres > 3000.0 )
			{
				xres = yres = 72.0;
				QFileInfo qfi(fn);
				message = QObject::tr("%1 may be corrupted : missing or wrong resolution tags").arg(qfi.fileName());
			}
			imgInfo.xres = qRound(xres);
			imgInfo.yres = qRound(yres);
			if (cinfo.output_components == 4)
				imgInfo.colorspace = 1;
			else if (cinfo.output_components == 3)
				imgInfo.colorspace = 0;
			else if (cinfo.output_components == 1)
				imgInfo.colorspace = 2;
			imgInfo.progressive = jpeg_has_multiple_scans(&cinfo);
			if ((!ExifInf.isNullThumbnail()) && (reqType == 4))
			{
				jpeg_destroy_decompress(&cinfo);
				fclose(infile);
				return true;
			}
			else
			{
				if (reqType == 4)
					reqType = 1;
			}
		}
		else
			imgInfo.exifDataValid = false;
		if (reqType == 4)
			reqType = 1;
#ifdef HAVE_CMS
		unsigned int EmbedLen = 0;
		unsigned char* EmbedBuffer;
		if (read_jpeg_marker(ICC_MARKER,&cinfo, &EmbedBuffer, &EmbedLen) && useEmbedded && CMSuse && useProf)
		{
			const char *Descriptor;
			tiffProf = cmsOpenProfileFromMem(EmbedBuffer, EmbedLen);
			Descriptor = cmsTakeProductDesc(tiffProf);
			imgInfo.profileName = QString(Descriptor);
			imgInfo.isEmbedded = true;
			free(EmbedBuffer);
		}
		else
		{
			imgInfo.isEmbedded = false;
			imgInfo.profileName = "";
		}
#endif // HAVE_CMS
		unsigned int PhotoshopLen = 0;
		unsigned char * PhotoshopBuffer;
		if (cinfo.density_unit == 0)
		{
			xres = 72;
			yres = 72;
			setDotsPerMeterX(2834);
			setDotsPerMeterY(2834);
		}
		else if ( cinfo.density_unit == 1 )
		{
			xres = cinfo.X_density;
			yres = cinfo.Y_density;
			setDotsPerMeterX( int(100. * cinfo.X_density / 2.54) );
			setDotsPerMeterY( int(100. * cinfo.Y_density / 2.54) );
		}
		else if ( cinfo.density_unit == 2 )
		{
			xres = cinfo.X_density * 2.54;
			yres = cinfo.Y_density * 2.54;
			setDotsPerMeterX( int(100. * cinfo.X_density) );
			setDotsPerMeterY( int(100. * cinfo.Y_density) );
		}
		if( xres <= 1.0 || yres <= 1.0 || xres > 3000.0 || yres > 3000.0 )
		{
			xres = yres = 72.0;
			setDotsPerMeterX(2834);
			setDotsPerMeterY(2834);
			QFileInfo qfi(fn);
			message = QObject::tr("%1 may be corrupted : missing or wrong resolution tags").arg(qfi.fileName());
		}
		imgInfo.xres = qRound(xres);
		imgInfo.yres = qRound(yres);
		if (cinfo.output_components == 4)
		{
			isCMYK = true;
			imgInfo.colorspace = 1;
		}
		else if (cinfo.output_components == 3)
			imgInfo.colorspace = 0;
		else if (cinfo.output_components == 1)
			imgInfo.colorspace = 2;
		imgInfo.progressive = jpeg_has_multiple_scans(&cinfo);

		if (read_jpeg_marker(PHOTOSHOP_MARKER,&cinfo, &PhotoshopBuffer, &PhotoshopLen) )
		{
			if (PhotoshopLen != 0)
			{
				bool savEx = imgInfo.exifDataValid;
				QByteArray arrayPhot(PhotoshopLen);
				arrayPhot.setRawData((const char*)PhotoshopBuffer,PhotoshopLen);
				QDataStream strPhot(arrayPhot,IO_ReadOnly);
				strPhot.setByteOrder( QDataStream::BigEndian );
				PSDHeader fakeHeader;
				fakeHeader.width = cinfo.output_width;
				fakeHeader.height = cinfo.output_height;
				if (cinfo.output_components == 4)
					imgInfo.colorspace = 1;
				else if (cinfo.output_components == 3)
					imgInfo.colorspace = 0;
				else if (cinfo.output_components == 1)
					imgInfo.colorspace = 2;
				imgInfo.progressive = jpeg_has_multiple_scans(&cinfo);
				parseRessourceData(strPhot, fakeHeader, PhotoshopLen);
				// Photoshop resolution is more accurate than jpeg header resolution
				xres = imgInfo.xres;
				yres = imgInfo.yres;
				setDotsPerMeterX( int(100. * imgInfo.xres / 2.54) );
				setDotsPerMeterY( int(100. * imgInfo.yres / 2.54) );
				if( xres <= 1.0 || yres <= 1.0 || xres > 3000.0 || yres > 3000.0 )
				{
					xres = yres = 72.0;
					imgInfo.xres = imgInfo.yres = 72;
//					imgInfo.xres = xres;
//					imgInfo.yres = yres;
					setDotsPerMeterX(2834);
					setDotsPerMeterY(2834);
					QFileInfo qfi(fn);
					message = QObject::tr("%1 may be corrupted : missing or wrong resolution tags").arg(qfi.fileName());
				}
				imgInfo.valid = (imgInfo.PDSpathData.size())>0?true:false; // The only interest is vectormask
				arrayPhot.resetRawData((const char*)PhotoshopBuffer,PhotoshopLen);
				free( PhotoshopBuffer );
				imgInfo.exifDataValid = savEx;
				fromPS = true;
			}
		}
		if ( cinfo.output_components == 3 || cinfo.output_components == 4)
			create( cinfo.output_width, cinfo.output_height, 32 );
		else if ( cinfo.output_components == 1 )
		{
			create( cinfo.output_width, cinfo.output_height, 8, 256 );
			for (int i=0; i<256; i++)
				setColor(i, qRgb(i,i,i));
		}
		if (!isNull())
		{
			uchar** lines = jumpTable();
			while (cinfo.output_scanline < cinfo.output_height)
				(void) jpeg_read_scanlines(&cinfo, lines + cinfo.output_scanline, cinfo.output_height);
			if ( cinfo.output_components == 3 )
			{
				for (uint j=0; j<cinfo.output_height; j++)
				{
					uchar *in = scanLine(j) + cinfo.output_width * 3;
					QRgb *out = (QRgb*)scanLine(j);
					for (uint i=cinfo.output_width; i--; )
					{
						in -= 3;
						out[i] = qRgb(in[0], in[1], in[2]);
					}
				}
			}
			if ( cinfo.output_components == 4 )
			{
				int method = 0;
				if (cinfo.jpeg_color_space == JCS_YCCK)
					method = 1;
				else if (fromPS)
				{
					if ((cinfo.jpeg_color_space == JCS_CMYK) && (cinfo.saw_Adobe_marker) && (cinfo.Adobe_transform == 0))
						method = 2;
				}
				else if ((cinfo.jpeg_color_space == JCS_CMYK) && (cinfo.saw_Adobe_marker))
					method = 1;
				for (int i = 0; i < height(); i++)
				{
					QRgb *ptr = (QRgb*)  scanLine(i);
					unsigned char c, m, y ,k;
					if (method == 1)
					{
						for (int j = 0; j <  width(); j++)
						{
							unsigned char *p = (unsigned char *) ptr;
							c = p[0];
							m = p[1];
							y =  p[2];
							k =  p[3];
							*ptr = qRgba(255 - c, 255 - m, 255 - y, 255 - k);
							ptr++;
						}
					}
					else if (method == 2)
					{
						for (int j = 0; j <  width(); j++)
						{
							unsigned char *p = (unsigned char *) ptr;
							c = p[0];
							m = p[1];
							y =  p[2];
							k =  p[3];
							*ptr = qRgba(255 - c, 255 - m, 255 - y, k);
							ptr++;
						}
					}
					else
					{
						for (int j = 0; j <  width(); j++)
						{
							unsigned char *p = (unsigned char *) ptr;
							c = p[0];
							m = p[1];
							y =  p[2];
							k =  p[3];
							*ptr = qRgba(y, m, c, k);
							ptr++;
						}
					}
				}
				isCMYK = true;
				if (realCMYK != 0)
					*realCMYK = true;
			}
			else
				isCMYK = false;
			if ( cinfo.output_components == 1 )
			{
				QImage tmpImg = convertDepth(32);
				create( cinfo.output_width, cinfo.output_height, 32 );
				for( int yi=0; yi < tmpImg.height(); ++yi )
				{
					QRgb *s = (QRgb*)(tmpImg.scanLine( yi ));
					QRgb *d = (QRgb*)(scanLine( yi ));
					for(int xi=0; xi < tmpImg.width(); ++xi )
					{
						(*d) = (*s);
						s++;
						d++;
					}
				}
			}
			setAlphaBuffer(true);
		}
		(void) jpeg_finish_decompress(&cinfo);
		fclose (infile);
		jpeg_destroy_decompress (&cinfo);
		imgInfo.layerInfo.clear();
		imgInfo.BBoxX = 0;
		imgInfo.BBoxH = height();
	}
	else
	{
		if (load(fn))
		{
			imgInfo.typ = 6;
			imgInfo.exifDataValid = false;
			xres = dotsPerMeterX() * 0.0254;
			yres = dotsPerMeterY() * 0.0254;
			int resInf = imgInfo.lowResType;
			*this = convertDepth(32);
			setAlphaBuffer(true);
			imgInfo.colorspace = 0;
			setDotsPerMeterX (QMAX(2834, (int) (xres / 0.0254)));
			setDotsPerMeterY (QMAX(2834, (int) (yres / 0.0254)));
			imgInfo.xres = QMAX(72, qRound(xres));
			imgInfo.yres = QMAX(72, qRound(yres));
			imgInfo.lowResType = resInf;
			imgInfo.BBoxX = 0;
			imgInfo.BBoxH = height();
		}
	}
	if (isNull())
		return  ret;
		
#ifdef HAVE_CMS
	if (CMSuse && useProf)
	{
		if (tiffProf)
			inputProf = tiffProf;
		else
		{
			QCString profilePath;
			if (isCMYK)
			{
				if (ScMW->InputProfilesCMYK.contains(Prof))
					imgInfo.profileName = Prof;
				else
					imgInfo.profileName = ScMW->doc->CMSSettings.DefaultImageCMYKProfile;
				profilePath = ScMW->InputProfilesCMYK[imgInfo.profileName].local8Bit();
			}
			else
			{
				if (ScMW->InputProfiles.contains(Prof))
					imgInfo.profileName = Prof;
				else
					imgInfo.profileName = ScMW->doc->CMSSettings.DefaultImageRGBProfile;
				profilePath = ScMW->InputProfiles[imgInfo.profileName].local8Bit();
			}
			inputProf = cmsOpenProfileFromFile(profilePath.data(), "r");
		}
	}
	if (CMSuse && useProf && inputProf)
	{
		DWORD SC_TYPE_YMCK_8 = (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1));//TYPE_YMCK_8;
		DWORD inputProfFormat = TYPE_BGRA_8;
		DWORD prnProfFormat   = SC_TYPE_YMCK_8;
		int inputProfColorSpace = static_cast<int>(cmsGetColorSpace(inputProf));
		if ( inputProfColorSpace == icSigRgbData )
			inputProfFormat = TYPE_BGRA_8;
		else if ( inputProfColorSpace == icSigCmykData )
			inputProfFormat = SC_TYPE_YMCK_8;
		else if ( inputProfColorSpace == icSigGrayData )
			inputProfFormat = TYPE_GRAY_8;
		int prnProfColorSpace = static_cast<int>(cmsGetColorSpace(CMSprinterProf));
		if ( prnProfColorSpace == icSigRgbData )
			prnProfFormat = TYPE_BGRA_8;
		else if ( prnProfColorSpace == icSigCmykData )
			prnProfFormat = SC_TYPE_YMCK_8;
		if (SoftProofing)
		{
			cmsProofFlags |= cmsFLAGS_SOFTPROOFING;
			if (Gamut)
				cmsProofFlags |= cmsFLAGS_GAMUTCHECK;
		}
		if (BlackPoint)
			cmsFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;

		xform = 0;
		switch (reqType)
		{
		case 0: // CMYK
			if (!isCMYK)
				xform = scCmsCreateTransform(inputProf, inputProfFormat, CMSprinterProf, prnProfFormat, IntentPrinter, cmsFlags);
			break;
		case 1: // RGB
			if (isCMYK) {
				if (systemBigEndian)
					swapByteOrder(3, 2, 1, 0);
				xform = scCmsCreateTransform(inputProf, inputProfFormat, CMSoutputProf, TYPE_BGRA_8, rend, cmsFlags);
			}
			break;
		case 2: // RGB Proof
			{
				if (SoftProofing)
				{
					xform = scCmsCreateProofingTransform(inputProf, inputProfFormat,
					                       CMSoutputProf, TYPE_BGRA_8, CMSprinterProf,
					                       IntentPrinter, rend, cmsFlags | cmsProofFlags);
				}
				else
					xform = scCmsCreateTransform(inputProf, inputProfFormat,
					                           CMSoutputProf, TYPE_BGRA_8, rend, cmsFlags);
			}
			break;
		case 3: // no Conversion just raw Data
			break;
		}
				
		if (xform)
		{
			if (systemBigEndian && reqType == 2 && isCMYK) {
				swapByteOrder(3, 2, 1, 0);
			}
			
			for (int i = 0; i < height(); i++)
			{
				LPBYTE ptr = scanLine(i);
				if ( inputProfFormat == TYPE_GRAY_8 && (reqType != 0) )
				{
					unsigned char* ucs = ptr + 1;
					unsigned char* uc = new unsigned char[width()];
					for( int uci = 0; uci < width(); uci++ )
					{
						uc[uci] = *ucs;
						ucs += 4;
					}
					cmsDoTransform(xform, uc, ptr, width());
					delete[] uc;
				}
				else if ( inputProfFormat == TYPE_GRAY_8 && (reqType == 0) )
				{
					unsigned char  value;
					unsigned char* ucs = ptr;
					for( int uci = 0; uci < width(); uci++ )
					{
						value = 255 - *(ucs + 1);
						ucs[0] = ucs[1] = ucs[2] = 0;
						ucs[3] = value;
						ucs += 4;
					}
				}
				else
					cmsDoTransform(xform, ptr, ptr, width());
			}
			
			if (systemBigEndian && reqType == 2 && isCMYK) {
				swapByteOrder(3, 2, 1, 0);
			}
			
			// if transforming from CMYK to RGB, flatten the alpha channel
			// which will still contain the black channel
			if (isCMYK && reqType != 0 && !bilevel)
			{
				QRgb alphaFF = qRgba(0,0,0,255);
				for (int i = 0; i < height(); i++)
				{
					LPBYTE ptr = scanLine(i);
					QRgb *p = (QRgb *) ptr;
					for (int j = 0; j < width(); j++, p++)
						*p |= alphaFF;
				}
			}
			cmsDeleteTransform (xform);
		}
		
		if (inputProf)
			cmsCloseProfile(inputProf);
	}
	else
#endif // HAVE_CMS
	{
		switch (reqType)
		{
		case 0:
			if (!isCMYK)
			{
				for (int i = 0; i < height(); i++)
				{
					QRgb *ptr = (QRgb *) scanLine(i);
					unsigned char cc, cm, cy ,ck;
					for (int j = 0; j < width(); j++)
					{
						cc = 255 - qRed(*ptr);
						cm = 255 - qGreen(*ptr);
						cy = 255 - qBlue(*ptr);
						ck = QMIN(QMIN(cc, cm), cy);
						*ptr++ = qRgba(cc-ck,cm-ck,cy-ck,ck);
					}
				}
			}
			break;
		case 1:
		case 2:
			if (isCMYK)
			{
				if (systemBigEndian)
				{
					for (int i = 0; i < height(); i++)
					{
						QRgb *ptr = (QRgb *) scanLine(i);
						unsigned char cr, cg, cb, ck;
						for (int j = 0; j < width(); j++)
						{
							unsigned char * quad = (unsigned char*) ptr;
							ck = quad[0];
							cr = 255 - QMIN(255, quad[1] + ck);
							cg = 255 - QMIN(255, quad[2] + ck);
							cb = 255 - QMIN(255, quad[3] + ck);
							*ptr++ = qRgba(cr,cg,cb,255);
						}
					}
				}
				else
				{
					for (int i = 0; i < height(); i++)
					{
						QRgb *ptr = (QRgb *) scanLine(i);
						unsigned char cr, cg, cb, ck;
						for (int j = 0; j < width(); j++)
						{
							ck = qAlpha(*ptr);
							cr = 255 - QMIN(255, qRed(*ptr) + ck);
							cg = 255 - QMIN(255, qGreen(*ptr) + ck);
							cb = 255 - QMIN(255, qBlue(*ptr) + ck);
							*ptr++ = qRgba(cr,cg,cb,255);
						}
					}
				}
			}
			break;
		case 3:
			break;
		}
	}
	if ((reqType == 0 || isCMYK) && !bilevel)
		setAlphaBuffer(false);
/*	setDotsPerMeterX (QMAX(2834, (int) (xres / 0.0254)));
	setDotsPerMeterY (QMAX(2834, (int) (yres / 0.0254)));
	imgInfo.xres = QMAX(72, qRound(xres));
	imgInfo.yres = QMAX(72, qRound(yres)); */
	if	(ScQApp->usingGUI() && !message.isEmpty() && showMsg)
	{
		QMessageBox::warning(ScMW, CommonStrings::trWarning, message, 1, 0, 0);
	}
	else if (!message.isEmpty())
		qWarning( "%s", message.local8Bit().data() );
	return true;
}
