Subversion Repositories Scribus

Rev

Rev 19605 | Rev 19737 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
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 "cmsettings.h"
#include "scclocale.h"
#include "scimage.h"

#include "scpaths.h"
#include "scribuscore.h"
#include "scimgdataloader_gimp.h"
#ifdef GMAGICK_FOUND
#include "scimgdataloader_gmagick.h"
#endif
#include "scimgdataloader_jpeg.h"
#include "scimgdataloader_ora.h"
#include "scimgdataloader_pict.h"
#include "scimgdataloader_ps.h"
#include "scimgdataloader_psd.h"
#include "scimgdataloader_pdf.h"
#include "scimgdataloader_pgf.h"
#include "scimgdataloader_qt.h"
#include "scimgdataloader_tiff.h"
#include "scimgdataloader_wpg.h"
#include "scstreamfilter_jpeg.h"
#include "sctextstream.h"
#include <QFile>
#include <QMessageBox>
#include <QList>
#include <QByteArray>
#include <QImageReader>
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <memory>
#include <setjmp.h>

#include "commonstrings.h"
#include "exif.h"
#include "sccolorengine.h"
#include "scimagecacheproxy.h"
#include "scstreamfilter.h"
#include "util.h"
#include "util_color.h"
#include "util_formats.h"
#include "util_ghostscript.h"
#include "rawimage.h"

using namespace std;

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


// ScImage will use implicit sharing:
ScImage::ScImage(const ScImage & image) : QImage(image.copy())
{
        initialize();
}


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

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

const QImage& ScImage::qImage()
{
        return *this;
}

QImage* ScImage::qImagePtr()
{
        return this;
}

QImage ScImage::scaled(int h, int w, Qt::AspectRatioMode mode, Qt::TransformationMode transformMode) const
{
        return QImage::scaled(h, w, mode, transformMode);
}


void ScImage::initialize()
{
        imgInfo.xres = 72;
        imgInfo.yres = 72;
        imgInfo.colorspace = ColorSpaceRGB;
        imgInfo.valid = false;
        imgInfo.isRequest = false;
        imgInfo.isEmbedded = false;
        imgInfo.progressive = 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.duotoneColors.clear();
        imgInfo.exifInfo.cameraName = "";
        imgInfo.exifInfo.cameraVendor = "";
        imgInfo.exifInfo.thumbnail = QImage();
        imgInfo.BBoxX = 0;
        imgInfo.BBoxH = 0;
}

ScImage::~ScImage()
{
}

void ScImage::applyEffect(const ScImageEffectList& effectsList, ColorList& colors, bool cmyk)
{
        ScribusDoc* doc = colors.document();
        if (effectsList.count() != 0)
        {
                for (int 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;
                                ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
                        //      fp >> col;
                                col = fp.readLine();
                                fp >> shading;
                                colorize(doc, colors[col], shading, cmyk);
                        }
                        if (effectsList.at(a).effectCode == EF_BRIGHTNESS)
                        {
                                QString tmpstr = effectsList.at(a).effectParameters;
                                int brightnessValue = 0;
                                ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
                                fp >> brightnessValue;
                                brightness(brightnessValue, cmyk);
                        }
                        if (effectsList.at(a).effectCode == EF_CONTRAST)
                        {
                                QString tmpstr = effectsList.at(a).effectParameters;
                                int contrastValue = 0;
                                ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
                                fp >> contrastValue;
                                contrast(contrastValue, cmyk);
                        }
                        if (effectsList.at(a).effectCode == EF_SHARPEN)
                        {
                                QString tmpstr = effectsList.at(a).effectParameters;
                                double radius, sigma;
                                ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
                                fp >> radius;
                                fp >> sigma;
                                sharpen(radius, sigma);
                        }
                        if (effectsList.at(a).effectCode == EF_BLUR)
                        {
                                QString tmpstr = effectsList.at(a).effectParameters;
                                double radius, sigma;
                                ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
                                fp >> radius;
                                fp >> sigma;
                                blur(static_cast<int>(radius));
                        }
                        if (effectsList.at(a).effectCode == EF_SOLARIZE)
                        {
                                QString tmpstr = effectsList.at(a).effectParameters;
                                double sigma;
                                ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
                                fp >> sigma;
                                solarize(sigma, cmyk);
                        }
                        if (effectsList.at(a).effectCode == EF_DUOTONE)
                        {
                                QString tmpstr = effectsList.at(a).effectParameters;
                                QString col1 = CommonStrings::None;
                                int shading1 = 100;
                                QString col2 = CommonStrings::None;
                                int shading2 = 100;
                                ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
                                col1 = fp.readLine();
                                col2 = fp.readLine();
                                fp >> shading1;
                                fp >> shading2;
                                int numVals;
                                double xval, yval;
                                FPointArray curve1;
                                curve1.resize(0);
                                fp >> numVals;
                                for (int nv = 0; nv < numVals; nv++)
                                {
                                        fp >> xval;
                                        fp >> yval;
                                        curve1.addPoint(xval, yval);
                                }
                                int lin1;
                                fp >> lin1;
                                FPointArray curve2;
                                curve2.resize(0);
                                fp >> numVals;
                                for (int nv = 0; nv < numVals; nv++)
                                {
                                        fp >> xval;
                                        fp >> yval;
                                        curve2.addPoint(xval, yval);
                                }
                                int lin2;
                                fp >> lin2;
                                duotone(doc, colors[col1], shading1, curve1, lin1, colors[col2], shading2, curve2, lin2, cmyk);
                        }
                        if (effectsList.at(a).effectCode == EF_TRITONE)
                        {
                                QString tmpstr = effectsList.at(a).effectParameters;
                                QString col1 = CommonStrings::None;
                                QString col2 = CommonStrings::None;
                                QString col3 = CommonStrings::None;
                                int shading1 = 100;
                                int shading2 = 100;
                                int shading3 = 100;
                                ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
                                col1 = fp.readLine();
                                col2 = fp.readLine();
                                col3 = fp.readLine();
                                fp >> shading1;
                                fp >> shading2;
                                fp >> shading3;
                                int numVals;
                                double xval, yval;
                                FPointArray curve1;
                                curve1.resize(0);
                                fp >> numVals;
                                for (int nv = 0; nv < numVals; nv++)
                                {
                                        fp >> xval;
                                        fp >> yval;
                                        curve1.addPoint(xval, yval);
                                }
                                int lin1;
                                fp >> lin1;
                                FPointArray curve2;
                                curve2.resize(0);
                                fp >> numVals;
                                for (int nv = 0; nv < numVals; nv++)
                                {
                                        fp >> xval;
                                        fp >> yval;
                                        curve2.addPoint(xval, yval);
                                }
                                int lin2;
                                fp >> lin2;
                                FPointArray curve3;
                                curve3.resize(0);
                                fp >> numVals;
                                for (int nv = 0; nv < numVals; nv++)
                                {
                                        fp >> xval;
                                        fp >> yval;
                                        curve3.addPoint(xval, yval);
                                }
                                int lin3;
                                fp >> lin3;
                                tritone(doc, colors[col1], shading1, curve1, lin1, colors[col2], shading2, curve2, lin2, colors[col3], shading3, curve3, lin3, cmyk);
                        }
                        if (effectsList.at(a).effectCode == EF_QUADTONE)
                        {
                                QString tmpstr = effectsList.at(a).effectParameters;
                                QString col1 = CommonStrings::None;
                                QString col2 = CommonStrings::None;
                                QString col3 = CommonStrings::None;
                                QString col4 = CommonStrings::None;
                                int shading1 = 100;
                                int shading2 = 100;
                                int shading3 = 100;
                                int shading4 = 100;
                                ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
                                col1 = fp.readLine();
                                col2 = fp.readLine();
                                col3 = fp.readLine();
                                col4 = fp.readLine();
                                fp >> shading1;
                                fp >> shading2;
                                fp >> shading3;
                                fp >> shading4;
                                int numVals;
                                double xval, yval;
                                FPointArray curve1;
                                curve1.resize(0);
                                fp >> numVals;
                                for (int nv = 0; nv < numVals; nv++)
                                {
                                        fp >> xval;
                                        fp >> yval;
                                        curve1.addPoint(xval, yval);
                                }
                                int lin1;
                                fp >> lin1;
                                FPointArray curve2;
                                curve2.resize(0);
                                fp >> numVals;
                                for (int nv = 0; nv < numVals; nv++)
                                {
                                        fp >> xval;
                                        fp >> yval;
                                        curve2.addPoint(xval, yval);
                                }
                                int lin2;
                                fp >> lin2;
                                FPointArray curve3;
                                curve3.resize(0);
                                fp >> numVals;
                                for (int nv = 0; nv < numVals; nv++)
                                {
                                        fp >> xval;
                                        fp >> yval;
                                        curve3.addPoint(xval, yval);
                                }
                                int lin3;
                                fp >> lin3;
                                FPointArray curve4;
                                curve4.resize(0);
                                fp >> numVals;
                                for (int nv = 0; nv < numVals; nv++)
                                {
                                        fp >> xval;
                                        fp >> yval;
                                        curve4.addPoint(xval, yval);
                                }
                                int lin4;
                                fp >> lin4;
                                quadtone(doc, colors[col1], shading1, curve1, lin1, colors[col2], shading2, curve2, lin2, colors[col3], shading3, curve3, lin3, colors[col4], shading4, curve4, lin4, cmyk);
                        }
                        if (effectsList.at(a).effectCode == EF_GRADUATE)
                        {
                                QString tmpstr = effectsList.at(a).effectParameters;
                                int numVals;
                                double xval, yval;
                                FPointArray curve;
                                curve.resize(0);
                                ScTextStream fp(&tmpstr, QIODevice::ReadOnly);
                                fp >> numVals;
                                for (int nv = 0; nv < numVals; nv++)
                                {
                                        fp >> xval;
                                        fp >> yval;
                                        curve.addPoint(xval, yval);
                                }
                                int lin;
                                fp >> lin;
                                doGraduate(curve, cmyk, lin);
                        }
                }
        }
}
/*
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)
{
        QVector<int> curveTable(256);
        int fk = qRound(255 / factor);
        for (int i = 0; i < 256; ++i)
        {
                curveTable[i] = qMin(255, static_cast<int>(i / fk) * fk);
        }
        applyCurve(curveTable, cmyk);
}

// Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
void ScImage::blur(int radius)
{
        if (radius < 1) {
                return;
        }

        QRgb *pix = (QRgb*)bits();
        int w   = width();
        int h   = height();
        int wm  = w-1;
        int hm  = h-1;
        int wh  = w*h;
        int div = radius+radius+1;

        int *r = new int[wh];
        int *g = new int[wh];
        int *b = new int[wh];
        int *a = new int[wh];
        int rsum, gsum, bsum, asum, x, y, i, yp, yi, yw;
        QRgb p;
        int *vmin = new int[qMax(w,h)];

        int divsum = (div+1)>>1;
        divsum *= divsum;
        int *dv = new int[256*divsum];
        for (i=0; i < 256*divsum; ++i) {
                dv[i] = (i/divsum);
        }

        yw = yi = 0;

        int **stack = new int*[div];
        for(int i = 0; i < div; ++i) {
                stack[i] = new int[4];
        }


        int stackpointer;
        int stackstart;
        int *sir;
        int rbs;
        int r1 = radius+1;
        int routsum, goutsum, boutsum, aoutsum;
        int rinsum, ginsum, binsum, ainsum;

        for (y = 0; y < h; ++y)
        {
                rinsum = ginsum = binsum = ainsum
                        = routsum = goutsum = boutsum = aoutsum
                        = rsum = gsum = bsum = asum = 0;
                for(i = -radius; i <= radius; ++i)
                {
                        p = pix[yi+qMin(wm,qMax(i,0))];
                        sir = stack[i+radius];
                        sir[0] = qRed(p);
                        sir[1] = qGreen(p);
                        sir[2] = qBlue(p);
                        sir[3] = qAlpha(p);

                        rbs = r1-abs(i);
                        rsum += sir[0]*rbs;
                        gsum += sir[1]*rbs;
                        bsum += sir[2]*rbs;
                        asum += sir[3]*rbs;

                        if (i > 0)
                        {
                                rinsum += sir[0];
                                ginsum += sir[1];
                                binsum += sir[2];
                                ainsum += sir[3];
                        }
                        else
                        {
                                routsum += sir[0];
                                goutsum += sir[1];
                                boutsum += sir[2];
                                aoutsum += sir[3];
                        }
                }
                stackpointer = radius;

                for (x=0; x < w; ++x)
                {

                        r[yi] = dv[rsum];
                        g[yi] = dv[gsum];
                        b[yi] = dv[bsum];
                        a[yi] = dv[asum];

                        rsum -= routsum;
                        gsum -= goutsum;
                        bsum -= boutsum;
                        asum -= aoutsum;

                        stackstart = stackpointer-radius+div;
                        sir = stack[stackstart%div];

                        routsum -= sir[0];
                        goutsum -= sir[1];
                        boutsum -= sir[2];
                        aoutsum -= sir[3];

                        if (y == 0)
                        {
                                vmin[x] = qMin(x+radius+1,wm);
                        }
                        p = pix[yw+vmin[x]];

                        sir[0] = qRed(p);
                        sir[1] = qGreen(p);
                        sir[2] = qBlue(p);
                        sir[3] = qAlpha(p);

                        rinsum += sir[0];
                        ginsum += sir[1];
                        binsum += sir[2];
                        ainsum += sir[3];

                        rsum += rinsum;
                        gsum += ginsum;
                        bsum += binsum;
                        asum += ainsum;

                        stackpointer = (stackpointer+1)%div;
                        sir = stack[(stackpointer)%div];

                        routsum += sir[0];
                        goutsum += sir[1];
                        boutsum += sir[2];
                        aoutsum += sir[3];

                        rinsum -= sir[0];
                        ginsum -= sir[1];
                        binsum -= sir[2];
                        ainsum -= sir[3];

                        ++yi;
                }
                yw += w;
        }
        for (x=0; x < w; ++x)
        {
                rinsum = ginsum = binsum = ainsum
                        = routsum = goutsum = boutsum = aoutsum
                        = rsum = gsum = bsum = asum = 0;

                yp =- radius * w;

                for(i=-radius; i <= radius; ++i)
                {
                        yi=qMax(0,yp)+x;

                        sir = stack[i+radius];

                        sir[0] = r[yi];
                        sir[1] = g[yi];
                        sir[2] = b[yi];
                        sir[3] = a[yi];

                        rbs = r1-abs(i);

                        rsum += r[yi]*rbs;
                        gsum += g[yi]*rbs;
                        bsum += b[yi]*rbs;
                        asum += a[yi]*rbs;

                        if (i > 0)
                        {
                                rinsum += sir[0];
                                ginsum += sir[1];
                                binsum += sir[2];
                                ainsum += sir[3];
                        }
                        else
                        {
                                routsum += sir[0];
                                goutsum += sir[1];
                                boutsum += sir[2];
                                aoutsum += sir[3];
                        }

                        if (i < hm)
                        {
                                yp += w;
                        }
                }

                yi = x;
                stackpointer = radius;

                for (y=0; y < h; ++y)
                {
                        pix[yi] = qRgba(dv[rsum], dv[gsum], dv[bsum], dv[asum]);

                        rsum -= routsum;
                        gsum -= goutsum;
                        bsum -= boutsum;
                        asum -= aoutsum;

                        stackstart = stackpointer-radius+div;
                        sir = stack[stackstart%div];

                        routsum -= sir[0];
                        goutsum -= sir[1];
                        boutsum -= sir[2];
                        aoutsum -= sir[3];

                        if (x==0)
                        {
                                vmin[y] = qMin(y+r1,hm)*w;
                        }
                        p = x+vmin[y];

                        sir[0] = r[p];
                        sir[1] = g[p];
                        sir[2] = b[p];
                        sir[3] = a[p];

                        rinsum += sir[0];
                        ginsum += sir[1];
                        binsum += sir[2];
                        ainsum += sir[3];

                        rsum += rinsum;
                        gsum += ginsum;
                        bsum += binsum;
                        asum += ainsum;

                        stackpointer = (stackpointer+1)%div;
                        sir = stack[stackpointer];

                        routsum += sir[0];
                        goutsum += sir[1];
                        boutsum += sir[2];
                        aoutsum += sir[3];

                        rinsum -= sir[0];
                        ginsum -= sir[1];
                        binsum -= sir[2];
                        ainsum -= sir[3];

                        yi += w;
                }
        }
        delete [] r;
        delete [] g;
        delete [] b;
        delete [] a;
        delete [] vmin;
        delete [] dv;

        for(int i = 0; i < div; ++i)
        {
                delete [] stack[i];
        }
        delete [] stack;
}

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 = QImage(width(), height(), QImage::Format_ARGB32);
        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];
        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;
                                        int px = pixel(mx, my);
                                        red += (*k)*(qRed(px)*257);
                                        green += (*k)*(qGreen(px)*257);
                                        blue += (*k)*(qBlue(px)*257);
                                        alpha += (*k)*(qAlpha(px)*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)
{
        QVector<int> curveTable(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(curveTable, cmyk);
}

void ScImage::brightness(int brightnessValue, bool cmyk)
{
        QVector<int> curveTable(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(curveTable, cmyk);
}

void ScImage::doGraduate(FPointArray curve, bool cmyk, bool linear)
{
        QVector<int> curveTable(256);
        for (int x = 0 ; x < 256 ; x++)
        {
                curveTable[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve, x / 255.0, linear) * 255)));
        }
        applyCurve(curveTable, cmyk);
}

void ScImage::applyCurve(const QVector<int>& curveTable, bool cmyk)
{
        int h = height();
        int w = width();
        QRgb *s;
        QRgb r;
        int c, m, y, k;
        unsigned char *p;
        for( int yi=0; yi < h; ++yi )
        {
                s = (QRgb*)(scanLine( yi ));
                for( int xi=0; xi < w; ++xi )
                {
                        r = *s;
                        if (cmyk)
                        {
                                p = (unsigned char *) s;
                                p[0] = 255 - curveTable[255 - p[0]];
                                p[1] = 255 - curveTable[255 - p[1]];
                                p[2] = 255 - curveTable[255 - p[2]];
                                p[3] = 255 - curveTable[255 - p[3]];
                        }
                        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(ScribusDoc* doc, 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;
        QRgb *s;
        QRgb r;
        double k;
        int cc2, cm2, cy2, k2;
        if (cmyk)
        {
                CMYKColor cmykCol;
                ScColorEngine::getShadeColorCMYK(color, doc, cmykCol, shade);
                cmykCol.getValues(cc, cm, cy, ck);
        }
        else
        {
                ck = 0;
                RGBColor rgbCol;
                ScColorEngine::getShadeColorRGB(color, doc, rgbCol, shade);
                rgbCol.getValues(cc, cm, cy);
        }
        for( int yi=0; yi < h; ++yi )
        {
                s = (QRgb*)(scanLine( yi ));
                for( int xi=0; xi < w; ++xi )
                {
                        r = *s;
                        if (cmyk)
                        {
                                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
                        {
                                k2 = 255 - qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r)), 255);
                                tmpR.setRgb(cc, cm, cy);
                                tmpR.getHsv(&hu, &sa, &v);
                                tmpR.setHsv(hu, sa * k2 / 255, 255 - ((255 - v) * k2 / 255));
                                tmpR.getRgb(&cc2, &cm2, &cy2);
                                *s = qRgba(cc2, cm2, cy2, qAlpha(r));
                        }
                        s++;
                }
        }
}

void ScImage::duotone(ScribusDoc* doc, ScColor color1, int shade1, FPointArray curve1, bool lin1, ScColor color2, int shade2, FPointArray curve2, bool lin2, bool cmyk)
{
        int h = height();
        int w = width();
        int c, c1, m, m1, y, y1, k, k1;
        int cn, c1n, mn, m1n, yn, y1n, kn, k1n;
        uchar cb;
        QVector<int> curveTable1;
        QVector<int> curveTable2;
        CMYKColor cmykCol;
        ScColorEngine::getShadeColorCMYK(color1, doc, cmykCol, shade1);
        cmykCol.getValues(c, m, y, k);
        ScColorEngine::getShadeColorCMYK(color2, doc, cmykCol, shade2);
        cmykCol.getValues(c1, m1, y1, k1);
        curveTable1.resize(256);
        for (int x = 0 ; x < 256 ; x++)
        {
                curveTable1[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve1, x / 255.0, lin1) * 255)));
        }
        curveTable2.resize(256);
        for (int x = 0 ; x < 256 ; x++)
        {
                curveTable2[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve2, x / 255.0, lin2) * 255)));
        }
        for( int yi=0; yi < h; ++yi )
        {
                QRgb * s = (QRgb*)(scanLine( yi ));
                for( int xi=0; xi < w; ++xi )
                {
                        QRgb r=*s;
                        if (cmyk)
                                cb = qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r) + qAlpha(r)), 255);
                        else
                                cb = 255 - qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r)), 255);
                        cn = qMin((c * curveTable1[(int)cb]) >> 8, 255);
                        mn = qMin((m * curveTable1[(int)cb]) >> 8, 255);
                        yn = qMin((y * curveTable1[(int)cb]) >> 8, 255);
                        kn = qMin((k * curveTable1[(int)cb]) >> 8, 255);
                        c1n = qMin((c1 * curveTable1[(int)cb]) >> 8, 255);
                        m1n = qMin((m1 * curveTable2[(int)cb]) >> 8, 255);
                        y1n = qMin((y1 * curveTable2[(int)cb]) >> 8, 255);
                        k1n = qMin((k1 * curveTable2[(int)cb]) >> 8, 255);
                        ScColor col = ScColor(qMin(cn+c1n, 255), qMin(mn+m1n, 255), qMin(yn+y1n, 255), qMin(kn+k1n, 255));
                        if (cmyk)
                                col.getCMYK(&cn, &mn, &yn, &kn);
                        else
                        {
                                col.getRawRGBColor(&cn, &mn, &yn);
                                kn = qAlpha(r);
                        }
                        *s = qRgba(cn, mn, yn, kn);
                        s++;
                }
        }
}

void ScImage::tritone(ScribusDoc* doc, ScColor color1, int shade1, FPointArray curve1, bool lin1, ScColor color2, int shade2, FPointArray curve2, bool lin2, ScColor color3, int shade3, FPointArray curve3, bool lin3, bool cmyk)
{
        int h = height();
        int w = width();
        int c, c1, c2, m, m1, m2, y, y1, y2, k, k1, k2;
        int cn, c1n, c2n, mn, m1n, m2n, yn, y1n, y2n, kn, k1n, k2n;
        uchar cb;
        CMYKColor cmykCol;
        QVector<int> curveTable1;
        QVector<int> curveTable2;
        QVector<int> curveTable3;
        ScColorEngine::getShadeColorCMYK(color1, doc, cmykCol, shade1);
        cmykCol.getValues(c, m, y, k);
        ScColorEngine::getShadeColorCMYK(color2, doc, cmykCol, shade2);
        cmykCol.getValues(c1, m1, y1, k1);
        ScColorEngine::getShadeColorCMYK(color3, doc, cmykCol, shade3);
        cmykCol.getValues(c2, m2, y2, k2);
        curveTable1.resize(256);
        for (int x = 0 ; x < 256 ; x++)
        {
                curveTable1[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve1, x / 255.0, lin1) * 255)));
        }
        curveTable2.resize(256);
        for (int x = 0 ; x < 256 ; x++)
        {
                curveTable2[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve2, x / 255.0, lin2) * 255)));
        }
        curveTable3.resize(256);
        for (int x = 0 ; x < 256 ; x++)
        {
                curveTable3[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve2, x / 255.0, lin3) * 255)));
        }
        for( int yi=0; yi < h; ++yi )
        {
                QRgb * s = (QRgb*)(scanLine( yi ));
                for( int xi=0; xi < w; ++xi )
                {
                        QRgb r=*s;
                        if (cmyk)
                                cb = qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r) + qAlpha(r)), 255);
                        else
                                cb = 255 - qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r)), 255);
                        cn = qMin((c * curveTable1[(int)cb]) >> 8, 255);
                        mn = qMin((m * curveTable1[(int)cb]) >> 8, 255);
                        yn = qMin((y * curveTable1[(int)cb]) >> 8, 255);
                        kn = qMin((k * curveTable1[(int)cb]) >> 8, 255);
                        c1n = qMin((c1 * curveTable2[(int)cb]) >> 8, 255);
                        m1n = qMin((m1 * curveTable2[(int)cb]) >> 8, 255);
                        y1n = qMin((y1 * curveTable2[(int)cb]) >> 8, 255);
                        k1n = qMin((k1 * curveTable2[(int)cb]) >> 8, 255);
                        c2n = qMin((c2 * curveTable3[(int)cb]) >> 8, 255);
                        m2n = qMin((m2 * curveTable3[(int)cb]) >> 8, 255);
                        y2n = qMin((y2 * curveTable3[(int)cb]) >> 8, 255);
                        k2n = qMin((k2 * curveTable3[(int)cb]) >> 8, 255);
                        ScColor col = ScColor(qMin(cn+c1n+c2n, 255), qMin(mn+m1n+m2n, 255), qMin(yn+y1n+y2n, 255), qMin(kn+k1n+k2n, 255));
                        if (cmyk)
                                col.getCMYK(&cn, &mn, &yn, &kn);
                        else
                        {
                                col.getRawRGBColor(&cn, &mn, &yn);
                                kn = qAlpha(r);
                        }
                        *s = qRgba(cn, mn, yn, kn);
                        s++;
                }
        }
}

void ScImage::quadtone(ScribusDoc* doc, ScColor color1, int shade1, FPointArray curve1, bool lin1, ScColor color2, int shade2, FPointArray curve2, bool lin2, ScColor color3, int shade3, FPointArray curve3, bool lin3, ScColor color4, int shade4, FPointArray curve4, bool lin4, bool cmyk)
{
        int h = height();
        int w = width();
        int c, c1, c2, c3, m, m1, m2, m3, y, y1, y2, y3, k, k1, k2, k3;
        int cn, c1n, c2n, c3n, mn, m1n, m2n, m3n, yn, y1n, y2n, y3n, kn, k1n, k2n, k3n;
        uchar cb;
        CMYKColor cmykCol;
        QVector<int> curveTable1;
        QVector<int> curveTable2;
        QVector<int> curveTable3;
        QVector<int> curveTable4;
        ScColorEngine::getShadeColorCMYK(color1, doc, cmykCol, shade1);
        cmykCol.getValues(c, m, y, k);
        ScColorEngine::getShadeColorCMYK(color2, doc, cmykCol, shade2);
        cmykCol.getValues(c1, m1, y1, k1);
        ScColorEngine::getShadeColorCMYK(color3, doc, cmykCol, shade3);
        cmykCol.getValues(c2, m2, y2, k2);
        ScColorEngine::getShadeColorCMYK(color4, doc, cmykCol, shade4);
        cmykCol.getValues(c3, m3, y3, k3);
        curveTable1.resize(256);
        for (int x = 0 ; x < 256 ; x++)
        {
                curveTable1[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve1, x / 255.0, lin1) * 255)));
        }
        curveTable2.resize(256);
        for (int x = 0 ; x < 256 ; x++)
        {
                curveTable2[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve2, x / 255.0, lin2) * 255)));
        }
        curveTable3.resize(256);
        for (int x = 0 ; x < 256 ; x++)
        {
                curveTable3[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve3, x / 255.0, lin3) * 255)));
        }
        curveTable4.resize(256);
        for (int x = 0 ; x < 256 ; x++)
        {
                curveTable4[x] = qMin(255, qMax(0, qRound(getCurveYValue(curve4, x / 255.0, lin4) * 255)));
        }
        for( int yi=0; yi < h; ++yi )
        {
                QRgb * s = (QRgb*)(scanLine( yi ));
                for( int xi=0; xi < w; ++xi )
                {
                        QRgb r=*s;
                        if (cmyk)
                                cb = qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r) + qAlpha(r)), 255);
                        else
                                cb = 255 - qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r)), 255);
                        cn = qMin((c * curveTable1[(int)cb]) >> 8, 255);
                        mn = qMin((m * curveTable1[(int)cb]) >> 8, 255);
                        yn = qMin((y * curveTable1[(int)cb]) >> 8, 255);
                        kn = qMin((k * curveTable1[(int)cb]) >> 8, 255);
                        c1n = qMin((c1 * curveTable2[(int)cb]) >> 8, 255);
                        m1n = qMin((m1 * curveTable2[(int)cb]) >> 8, 255);
                        y1n = qMin((y1 * curveTable2[(int)cb]) >> 8, 255);
                        k1n = qMin((k1 * curveTable2[(int)cb]) >> 8, 255);
                        c2n = qMin((c2 * curveTable3[(int)cb]) >> 8, 255);
                        m2n = qMin((m2 * curveTable3[(int)cb]) >> 8, 255);
                        y2n = qMin((y2 * curveTable3[(int)cb]) >> 8, 255);
                        k2n = qMin((k2 * curveTable3[(int)cb]) >> 8, 255);
                        c3n = qMin((c3 * curveTable4[(int)cb]) >> 8, 255);
                        m3n = qMin((m3 * curveTable4[(int)cb]) >> 8, 255);
                        y3n = qMin((y3 * curveTable4[(int)cb]) >> 8, 255);
                        k3n = qMin((k3 * curveTable4[(int)cb]) >> 8, 255);
                        ScColor col = ScColor(qMin(cn+c1n+c2n+c3n, 255), qMin(mn+m1n+m2n+m3n, 255), qMin(yn+y1n+y2n+y3n, 255), qMin(kn+k1n+k2n+k3n, 255));
                        if (cmyk)
                                col.getCMYK(&cn, &mn, &yn, &kn);
                        else
                        {
                                col.getRawRGBColor(&cn, &mn, &yn);
                                kn = qAlpha(r);
                        }
                        *s = qRgba(cn, mn, yn, kn);
                        s++;
                }
        }
}

void ScImage::invert(bool cmyk)
{
        int h = height();
        int w = width();
        unsigned char *p;
        QRgb * s;
        unsigned char c, m, y, k;
        for( int yi=0; yi < h; ++yi )
        {
                s = (QRgb*)(scanLine( yi ));
                for( int xi=0; xi < w; ++xi )
                {
                        if (cmyk)
                        {
                                p = (unsigned char *) s;
                                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();
        int k;
        QRgb * s;
        QRgb r;
        for( int yi=0; yi < h; ++yi )
        {
                s = (QRgb*)(scanLine( yi ));
                for( int xi=0; xi < w; ++xi )
                {
                        r = *s;
                        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);
                                *s = qRgba(k, k, k, qAlpha(r));
                        }
                        s++;
                }
        }
}

void ScImage::swapRGBA()
{
        unsigned int *ptr;
        unsigned char *p;
        unsigned char r, b;
        for (int i = 0; i < height(); ++i)
        {
                ptr = (unsigned int *) scanLine(i);
                for (int j = 0; j < width(); ++j)
                {
                        p = (unsigned char *) ptr;
                        r = p[0];
                        b = p[2];
                        p[2] = r;
                        p[0] = b;
                        ptr++;
                }
        }
}

bool ScImage::createLowRes(double scale)
{
        int w = qRound(width() / scale);
        int h = qRound(height() / scale);
        if (w >= width() && h >= height())  // don't do unnecessary scaling
                return false;
        QImage tmp = scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
        if (tmp.format() != QImage::Format_ARGB32)
                tmp = tmp.convertToFormat(QImage::Format_ARGB32);
        QImage::operator=(tmp);
        return true;
}

bool ScImage::convert2JPG(QString fn, int Quality, bool isCMYK, bool isGray)
{
        bool success = false;
        QFile file(fn);
        if (file.open(QIODevice::WriteOnly))
        {
                ScJpegEncodeFilter::Color imgColor = ScJpegEncodeFilter::GRAY;
                if (isCMYK)
                        imgColor = ScJpegEncodeFilter::CMYK;
                else if (!isGray)
                        imgColor = ScJpegEncodeFilter::RGB;
                int qual[] = { 95, 85, 75, 50, 25 };  // These are the JPEG Quality settings 100 means best, 0 .. don't discuss
                QDataStream dataStream(&file);
                ScJpegEncodeFilter jpegFilter(&dataStream, width(), height(), imgColor);
                jpegFilter.setQuality(qual[Quality]);
                if (jpegFilter.openFilter())
                {
                        if (isCMYK)
                                success = writeCMYKDataToFilter(&jpegFilter);
                        else if (isGray)
                                success = writeGrayDataToFilter(&jpegFilter, true);
                        else
                                success = writeRGBDataToFilter(&jpegFilter);
                        success &= jpegFilter.closeFilter();
                }
                file.close();
        }
        return success;
}

QByteArray ScImage::ImageToArray()
{
        int i = 0;
        int h = height();
        int w = width();
        unsigned char u;
        QRgb *s;
        QRgb r;
        QByteArray imgArray(3 * h * w, ' ');
        if (imgArray.isNull())
                return imgArray;
        for( int yi=0; yi < h; ++yi )
        {
                s = (QRgb*)(scanLine( yi ));
                for( int xi=0; xi < w; ++xi )
                {
                        r = *s++;
                        u=qRed(r);
                        imgArray[i++] = u;
                        u=qGreen(r);
                        imgArray[i++] = u;
                        u=qBlue(r);
                        imgArray[i++] = u;
                }
        }
        return imgArray;
}

void ScImage::convertToGray(void)
{
        int k;
        int h = height();
        int w = width();
        QRgb *s, r;
        for( int yi=0; yi < h; ++yi )
        {
                s = (QRgb*)(scanLine( yi ));
                for( int xi=0; xi < w; ++xi )
                {
                        r = *s;
                        k = qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r)), 255);
                        *s++ = qRgba(k, 0, 0, 0);
                }
        }
}

bool ScImage::writeRGBDataToFilter(ScStreamFilter* filter)
{
        QRgb r, *s;
        QByteArray buffer;
        bool success = true;
        int  h = height();
        int  w = width();
        int  pending = 0;
        int  scanLineSize = (3 * w);
        int  bufferSize   = qMax(scanLineSize, (65536 - 65536 % scanLineSize));
        buffer.resize(bufferSize + 16);
        if (buffer.isNull()) // Memory allocation failure
                return false;
        for( int yi=0; yi < h; ++yi )
        {
                s = (QRgb*)(scanLine( yi ));
                for( int xi=0; xi < w; ++xi )
                {
                        r = *s++;
                        buffer[pending++] = static_cast<unsigned char>(qRed(r));
                        buffer[pending++] = static_cast<unsigned char>(qGreen(r));
                        buffer[pending++] = static_cast<unsigned char>(qBlue(r));
                }
                if (pending >= bufferSize)
                {
                        success &= filter->writeData(buffer.constData(), pending);
                        pending = 0;
                }
        }
        if (pending)
                success &= filter->writeData(buffer.constData(), pending);
        return success;
}

bool ScImage::writeGrayDataToFilter(ScStreamFilter* filter, bool precal)
{
        QRgb r, *s;
        QByteArray buffer;
        bool success = true;
        int  h = height();
        int  w = width();
        int  pending = 0, k;
        int  scanLineSize = w;
        int  bufferSize   = qMax(scanLineSize, (65536 - 65536 % scanLineSize));
        buffer.resize(bufferSize + 16);
        if (buffer.isNull()) // Memory allocation failure
                return false;
        for( int yi=0; yi < h; ++yi )
        {
                s = (QRgb*)(scanLine( yi ));
                if (precal) // image data is already grayscale, no need for weighted conversion
                {
                        for( int xi=0; xi < w; ++xi )
                        {
                                r = *s;
                                k = qRed(r);
                                buffer[pending++] = k;
                                s++;
                        }
                }
                else
                {
                        for( int xi=0; xi < w; ++xi )
                        {
                                r = *s;
                                k = qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r)), 255);
                                buffer[pending++] = k;
                                s++;
                        }
                }
                if (pending >= bufferSize)
                {
                        success &= filter->writeData(buffer.constData(), pending);
                        pending = 0;
                }
        }
        if (pending)
                success &= filter->writeData(buffer.constData(), pending);
        return success;
}

bool ScImage::writeMonochromeDataToFilter(ScStreamFilter* filter, bool fromCmyk)
{
        QRgb *s;
        QByteArray buffer;
        bool success = true;
        int  h = height();
        int  w = width();
        int  byteCount = 0;
        int  bufferSize = (w + 7) / 8 * h;
        int  value;
        const unsigned char threshold = 127;
        buffer.resize(bufferSize);
        if (buffer.isNull()) // Memory allocation failure
                return false;
        for (int yi = 0; yi < h; ++yi)
        {
                char curByte = 0;
                int bitCount = 0;
                s = (QRgb*)(scanLine( yi ));
                for (int xi = 0; xi < w; ++xi)
                {
                        curByte <<= 1;
                        value = fromCmyk ? (255 - qAlpha(*s)) : qRed(*s);
                        if (value > threshold) // In monochrome images all elements have the same value.
                                curByte |= 1;
                        ++bitCount;
                        if (bitCount == 8)
                        {
                                buffer[byteCount++] = curByte;
                                curByte = 0;
                                bitCount = 0;
                        }
                        ++s;
                }
                // End of line is aligned to byte.
                if (bitCount > 0) {
                        curByte <<=  8-bitCount;
                        buffer[byteCount++] = curByte;
                }
        }
        assert(byteCount == bufferSize);
        success = filter->writeData(buffer.constData(), byteCount);
        return success;
}

bool ScImage::writeCMYKDataToFilter(ScStreamFilter* filter)
{
        QRgb r, *s;
        QByteArray buffer;
        bool success = true;
        int  h = height();
        int  w = width();
        int  pending = 0;
        int  scanLineSize = (4 * w);
        int  bufferSize   = qMax(scanLineSize, (65536 - 65536 % scanLineSize));
        buffer.resize(bufferSize + 16);
        if (buffer.isNull()) // Memory allocation failure
                return false;
        for( int yi=0; yi < h; ++yi )
        {
                s = (QRgb*)(scanLine( yi ));
                for( int xi=0; xi < w; ++xi )
                {
                        r = *s++;
                        buffer[pending++] = static_cast<unsigned char> (qRed(r));
                        buffer[pending++] = static_cast<unsigned char> (qGreen(r));
                        buffer[pending++] = static_cast<unsigned char> (qBlue(r));
                        buffer[pending++] = static_cast<unsigned char> (qAlpha(r));
                }
                if (pending >= bufferSize)
                {
                        success &= filter->writeData(buffer.constData(), pending);
                        pending = 0;
                }
        }
        if (pending)
                success &= filter->writeData(buffer.constData(), pending);
        return success;
}

bool ScImage::writePSImageToFilter(ScStreamFilter* filter, int pl)
{
        QRgb r, *s;
        QByteArray buffer;
        bool success = true;
        int  c, m, y, k;
        int  h = height();
        int  w = width();
        int  pending = 0;
        int  scanLineSize = (pl == -1) ? (4 * w) : w;
        int  bufferSize   = qMax(scanLineSize, (65536 - 65536 % scanLineSize));
        buffer.resize(bufferSize + 16);
        if (buffer.isNull()) // Memory allocation failure
                return false;
        for( int yi=0; yi < h; ++yi )
        {
                s = (QRgb*)(scanLine( yi ));
                for( int xi=0; xi < w; ++xi )
                {
                        r = *s++;
                        c = qRed(r);
                        m = qGreen(r);
                        y = qBlue(r);
                        k = qAlpha(r);
                        if (pl == -1)
                        {
                                buffer[pending++] = static_cast<unsigned char> (c);
                                buffer[pending++] = static_cast<unsigned char> (m);
                                buffer[pending++] = static_cast<unsigned char> (y);
                                buffer[pending++] = static_cast<unsigned char> (k);
                        }
                        else
                        {
                                if (pl == -2)
                                        buffer[pending++] = static_cast<unsigned char> (qMin(255, qRound(0.3 * c + 0.59 * m + 0.11 * y + k)));
                                if (pl == 1)
                                        buffer[pending++] = static_cast<unsigned char> (c);
                                if (pl == 2)
                                        buffer[pending++] = static_cast<unsigned char> (m);
                                if (pl == 3)
                                        buffer[pending++] = static_cast<unsigned char> (y);
                                if (pl == 0)
                                        buffer[pending++] = static_cast<unsigned char> (k);
                        }
                }
                if (pending >= bufferSize)
                {
                        success &= filter->writeData(buffer.constData(), pending);
                        pending = 0;
                }
        }
        if (pending)
                success &= filter->writeData(buffer.constData(), pending);
        return success;
}

bool ScImage::writePSImageToFilter(ScStreamFilter* filter, const QByteArray& mask, int pl)
{
        QRgb r, *s;
        QByteArray buffer;
        bool success = true;
        int  c, m, y, k;
        int  h = height();
        int  w = width();
        int  pending = 0;
        int  scanLineSize = (pl == -1) ? (5 * w) : (2 * w);
        int  bufferSize   = qMax(scanLineSize, (65536 - 65536 % scanLineSize));
        if (mask.size() < (h * w))  // Check if mask provide enough data
                return false;
        buffer.resize(bufferSize + 16);
        if (buffer.isNull()) // Check for memory allocation failure
                return false;
        unsigned char* maskData = (unsigned char*) mask.constData();
        for( int yi=0; yi < h; ++yi )
        {
                s = (QRgb*)(scanLine( yi ));
                for( int xi=0; xi < w; ++xi )
                {
                        r = *s++;
                        c = qRed(r);
                        m = qGreen(r);
                        y = qBlue(r);
                        k = qAlpha(r);
                        if (pl == -1)
                        {
                                buffer[pending++] = *maskData++;
                                buffer[pending++] = static_cast<unsigned char> (c);
                                buffer[pending++] = static_cast<unsigned char> (m);
                                buffer[pending++] = static_cast<unsigned char> (y);
                                buffer[pending++] = static_cast<unsigned char> (k);
                        }
                        else
                        {
                                buffer[pending++] = *maskData++;
                                if (pl == -2)
                                        buffer[pending++] = static_cast<unsigned char> (qMin(255, qRound(0.3 * c + 0.59 * m + 0.11 * y + k)));
                                if (pl == 1)
                                        buffer[pending++] = static_cast<unsigned char> (c);
                                if (pl == 2)
                                        buffer[pending++] = static_cast<unsigned char> (m);
                                if (pl == 3)
                                        buffer[pending++] = static_cast<unsigned char> (y);
                                if (pl == 0)
                                        buffer[pending++] = static_cast<unsigned char> (k);
                        }
                }
                if (pending >= bufferSize)
                {
                        success &= filter->writeData(buffer.constData(), pending);
                        pending = 0;
                }
        }
        if (pending)
                success &= filter->writeData(buffer.constData(), pending);
        return success;
}

void ScImage::scaleImage(int nwidth, int nheight)
{
        int depth = this->depth();
        if (depth == 32)
        {
                scaleImage32bpp(nwidth, nheight);
                return;
        }
        scaleImageGeneric(nwidth, nheight);
}

void ScImage::scaleImage32bpp(int nwidth, int nheight)
{
        QImage dst(nwidth, nheight, QImage::Format_ARGB32);
        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;

        int depth = this->depth();
        if (depth != 32)
        {
                QImage::operator=(QImage::scaled(nwidth, nheight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
                return;
        }

        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++);
                        QRgb *nxPEnd = nxP + newcols;
                        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 += fraccoltofill * qAlpha( *xP );
                                r += fraccoltofill * qRed( *xP );
                                g += fraccoltofill * qGreen( *xP );
                                b += fraccoltofill * qBlue( *xP );
                        }
                        if (nxP < nxPEnd)
                        {
                                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 );
                                while (++nxP != nxPEnd)
                                        nxP[0] = nxP[-1];
                        }
                }
        }
        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;
        QImage::operator=(QImage(nwidth, nheight, QImage::Format_ARGB32));
        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;
}

void ScImage::scaleImageGeneric(int nwidth, int nheight)
{
        unsigned char* xelrow = 0;
        unsigned char* tempxelrow = 0;
        register unsigned char* xP;
        register unsigned char* 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* ps;
        int rowswritten = 0;

        int depth = this->depth();
        Format imgFormat = this->format();
        bool execScaled = (depth == 1 || depth == 4 || depth == 16);
        execScaled |= (imgFormat == QImage::Format_ARGB8565_Premultiplied);
        execScaled |= (imgFormat == QImage::Format_RGB666);
        execScaled |= (imgFormat == QImage::Format_ARGB6666_Premultiplied);
        execScaled |= (imgFormat == QImage::Format_ARGB8555_Premultiplied);
        if (execScaled)
        {
                QImage::operator=(QImage::scaled(nwidth, nheight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
                return;
        }

        QImage dst(nwidth, nheight, this->format());
        int nChannels = this->depth() / 8;

        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 unsigned char[cols * nChannels];
        ps = new long[cols];

        for (int chIndex = 0; chIndex < nChannels; ++chIndex)
        {
                xelrow = 0;
                rowsread = rowswritten = 0;
                fracrowleft = syscale;
                needtoreadrow = 1;
                for (col = 0; col < cols; ++col)
                        ps[col] = HALFSCALE;
                fracrowtofill = SCALE;
                for (row = 0; row < newrows; ++row)
                {
                        if (newrows == rows)
                                tempxelrow = xelrow = scanLine(rowsread++);
                        else
                        {
                                while (fracrowleft < fracrowtofill)
                                {
                                        if (needtoreadrow && rowsread < rows)
                                                xelrow = scanLine(rowsread++);
                                        for (col = 0, xP = xelrow + chIndex; col < cols; ++col, xP += nChannels)
                                                ps[col] += fracrowleft * (*xP);
                                        fracrowtofill -= fracrowleft;
                                        fracrowleft = syscale;
                                        needtoreadrow = 1;
                                }
                                if (needtoreadrow && rowsread < rows)
                                {
                                        xelrow = scanLine(rowsread++);
                                        needtoreadrow = 0;
                                }
                                register long p = 0;
                                xP  = xelrow + chIndex;
                                nxP = tempxelrow + chIndex;
                                for (col = 0; col < cols; ++col, xP += nChannels, nxP += nChannels)
                                {
                                        p = ps[col] + fracrowtofill * (*xP);
                                        p /= SCALE;
                                        if ( p > maxval ) p = maxval;
                                        *nxP = (unsigned char) p;
                                        ps[col] = HALFSCALE;
                                }
                                fracrowleft -= fracrowtofill;
                                if ( fracrowleft == 0 )
                                {
                                        fracrowleft = syscale;
                                        needtoreadrow = 1;
                                }
                                fracrowtofill = SCALE;
                        }
                        if (newcols == cols)
                        {
                                memcpy(dst.scanLine(rowswritten++), tempxelrow, newcols * nChannels);
                        }
                        else
                        {
                                register long p;
                                register long fraccoltofill, fraccolleft = 0;
                                register int needcol;
                                nxP = dst.scanLine(rowswritten++) + chIndex;
                                unsigned char *nxPEnd = nxP + newcols * nChannels;
                                fraccoltofill = SCALE;
                                p = HALFSCALE;
                                needcol = 0;
                                for (col = 0, xP = tempxelrow + chIndex; col < cols; ++col, xP += nChannels)
                                {
                                        fraccolleft = sxscale;
                                        while ( fraccolleft >= fraccoltofill )
                                        {
                                                if ( needcol )
                                                {
                                                        nxP += nChannels;
                                                        p = HALFSCALE;
                                                }
                                                p += fraccoltofill * ( *xP );
                                                p /= SCALE;
                                                if ( p > maxval ) p = maxval;
                                                *nxP = (unsigned char) p;
                                                fraccolleft -= fraccoltofill;
                                                fraccoltofill = SCALE;
                                                needcol = 1;
                                        }
                                        if (fraccolleft > 0)
                                        {
                                                if (needcol)
                                                {
                                                        nxP += nChannels;
                                                        p = HALFSCALE;
                                                        needcol = 0;
                                                }
                                                p += fraccolleft * (*xP);
                                                fraccoltofill -= fraccolleft;
                                        }
                                }
                                if (fraccoltofill > 0)
                                {
                                        xP -= nChannels;
                                        p += fraccoltofill * (*xP);
                                }
                                if (nxP < nxPEnd)
                                {
                                        p /= SCALE;
                                        if ( p > maxval ) p = maxval;
                                        *nxP = (unsigned char) p;
                                        while ((nxP += nChannels) != nxPEnd)
                                                nxP[0] = nxP[-nChannels];
                                }
                        }
                }
        }
        if (newrows != rows && tempxelrow)// Robust, tempxelrow might be 0 1 day
                delete [] tempxelrow;
        if (ps)                         // Avoid purify complaint
                delete [] ps;

        int scanWidth = dst.width() * nChannels;
        QImage::operator=(QImage(nwidth, nheight, this->format()));
        for( int yi=0; yi < dst.height(); ++yi )
        {
                uchar *s = (dst.scanLine( yi ));
                uchar *d = (scanLine( yi ));
                memcpy(d, s, scanWidth);
        }
        return;
}

bool ScImage::getAlpha(QString fn, int page, QByteArray& alpha, bool PDF, bool pdf14, int gsRes, int scaleXSize, int scaleYSize)
{
        bool gotAlpha = false;
        auto_ptr<ScImgDataLoader> pDataLoader;
        imgInfo.valid = false;
        imgInfo.clipPath = "";
        imgInfo.PDSpathData.clear();
        imgInfo.layerInfo.clear();
        QFileInfo fi = QFileInfo(fn);
        if (!fi.exists())
                return false;
        alpha.resize(0);
        QString ext = fi.suffix().toLower();
        QString ext2 = getImageType(fn);
        if (ext.isEmpty() || (ext2 != ext))
                ext = ext2;
        QList<QByteArray> fmtList = QImageReader::supportedImageFormats();
        QStringList fmtImg;
        for (int i = 0; i < fmtList.count(); i++)
        {
                fmtImg.append( QString(fmtList[i].toLower()) );
        }
        if (extensionIndicatesJPEG(ext))
                return true;
        if (extensionIndicatesPDF(ext))
        {
                pDataLoader.reset( new ScImgDataLoader_PDF() );
        }
        else if (extensionIndicatesEPSorPS(ext))
        {
                pDataLoader.reset( new ScImgDataLoader_PS() );
        }
        else if (extensionIndicatesTIFF(ext))
        {
                pDataLoader.reset( new ScImgDataLoader_TIFF() );
                if      (pDataLoader.get())
                        pDataLoader->setRequest(imgInfo.isRequest, imgInfo.RequestProps);
        }
        else if (extensionIndicatesPSD(ext))
        {
                pDataLoader.reset( new ScImgDataLoader_PSD() );
                if      (pDataLoader.get())
                        pDataLoader->setRequest(imgInfo.isRequest, imgInfo.RequestProps);
        }
        else if (ext == "ora")
        {
                pDataLoader.reset( new ScImgDataLoader_ORA() );
                if      (pDataLoader.get())
                        pDataLoader->setRequest(imgInfo.isRequest, imgInfo.RequestProps);
        }
        else if (ext == "pat")
                pDataLoader.reset( new ScImgDataLoader_GIMP() );
        else if (ext == "pgf")
                pDataLoader.reset( new ScImgDataLoader_PGF() );
        else if ((ext == "pct") || (ext == "pic") || (ext == "pict"))
                pDataLoader.reset( new ScImgDataLoader_PICT() );
        else if (ext == "wpg")
                pDataLoader.reset( new ScImgDataLoader_WPG() );
#ifdef GMAGICK_FOUND
#warning "Compiling with GraphicsMagick support!"
        else if (fmtImg.contains(ext))
                pDataLoader.reset( new ScImgDataLoader_QT() );
        else
                pDataLoader .reset( new ScImgDataLoader_GMagick() );
#else
        else
                pDataLoader.reset( new ScImgDataLoader_QT() );
#endif

        if (pDataLoader.get())
        {
                bool hasAlpha    = false;
                bool alphaLoaded = pDataLoader->preloadAlphaChannel(fn, page, gsRes, hasAlpha);
                if (!alphaLoaded || !hasAlpha)
                        return alphaLoaded;
                QImage rImage;
                if (pDataLoader->useRawImage() || extensionIndicatesPSD(ext) || extensionIndicatesTIFF(ext))
                {
                        if (pDataLoader->imageInfoRecord().valid)
                        {
                                if ((pDataLoader->r_image.channels() == 5) || (pDataLoader->imageInfoRecord().colorspace == ColorSpaceCMYK))
                                        rImage = pDataLoader->r_image.convertToQImage(true);
                                else
                                        rImage = pDataLoader->r_image.convertToQImage(false);
                        }
                }
                else
                        rImage = pDataLoader->image();
                if (rImage.isNull())
                        return false;
                if ((scaleXSize != 0) && (scaleYSize != 0) && (scaleXSize != rImage.width() || scaleYSize != rImage.height()))
                        rImage = rImage.scaled(scaleXSize, scaleYSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
                int i = 0, w2;
                unsigned char u;
                int hm = rImage.height();
                int wm = rImage.width();
                QRgb r, *s;
                if (pdf14)
                {
                        alpha.resize(hm * wm);
                        if (alpha.size() > 0) //
                        {
                                for( int yi=0; yi < hm; ++yi )
                                {
                                        s = (QRgb*)(rImage.scanLine( yi ));
                                        for( int xi=0; xi < wm; ++xi )
                                        {
                                                r = *s++;
                                                u = qAlpha(r);
                                                alpha[i++] = u;
                                        }
                                }
                                gotAlpha = true;
                        }
                }
                else
                {
                        uchar * s;
                        QImage iMask = rImage.createAlphaMask();
                        iMask = iMask.convertToFormat(QImage::Format_Mono);
                        hm = iMask.height();
                        wm = iMask.width();
                        w2 = wm / 8;
                        if ((wm % 8) != 0)
                                w2++;
                        alpha.resize(hm * w2);
                        if (alpha.size() > 0)
                        {
                                for( int yi=0; yi < hm; ++yi )
                                {
                                        s = iMask.scanLine( yi );
                                        for( int xi=0; xi < w2; ++xi )
                                        {
                                                u = *(s+xi);
                                                if(PDF) u = ~u;
                                                alpha[i++] = u;
                                        }
                                }
                                gotAlpha = true;
                        }
                }
        }
        return gotAlpha;
}

void ScImage::getEmbeddedProfile(const QString & fn, QByteArray *profile, int *components, int page)
{
        Q_ASSERT(profile);
        Q_ASSERT(components);
        ScColorProfile prof;
        ScImgDataLoader* pDataLoader = NULL;

        profile->resize(0);
        *components = 0;
        QFileInfo fi = QFileInfo(fn);
        if (!fi.exists())
                return;
        QString ext = fi.suffix().toLower();
        QString ext2 = getImageType(fn);
        if (ext.isEmpty() || (ext2 != ext))
                ext = ext2;

        if (extensionIndicatesPSD(ext))
                pDataLoader = new ScImgDataLoader_PSD();
        else if (extensionIndicatesEPSorPS(ext))
                pDataLoader = new ScImgDataLoader_PS();
        else if (extensionIndicatesTIFF(ext))
                pDataLoader = new ScImgDataLoader_TIFF();
        else if (extensionIndicatesJPEG(ext))
                pDataLoader = new ScImgDataLoader_JPEG();
        #ifdef FOUND_GMAGICK
        else
                pDataLoader = new ScImgDataLoader_GMagick();
        #endif

        if (pDataLoader)
        {
                pDataLoader->loadEmbeddedProfile(fn, page);
                QByteArray embeddedProfile = pDataLoader->embeddedProfile();
                if      (embeddedProfile.size())
                {
                        prof = ScCore->defaultEngine.openProfileFromMem(embeddedProfile);
                        if (prof)
                        {
                                if (prof.colorSpace() == ColorSpace_Rgb)
                                        *components = 3;
                                if (prof.colorSpace() == ColorSpace_Cmyk)
                                        *components = 4;
                                if (prof.colorSpace() == ColorSpace_Gray)
                                        *components = 1;
                                *profile = embeddedProfile;
                        }
                }
                delete pDataLoader;
        }
}

void ScImage::addProfileToCacheModifiers(ScImageCacheProxy & cache, const QString & prefix, const ScColorProfile & profile) const
{
        if (profile)
        {
                cache.addModifier(prefix + "ProfileDescription", profile.productDescription());
                const ScColorProfileData *pd = profile.data();
                if (pd)
                {
                        QString hash = pd->dataHash();
                        if (!hash.isEmpty())
                                cache.addModifier(prefix + "ProfileHash", hash);
                }
        }
}

bool ScImage::loadPicture(ScImageCacheProxy & cache, bool & fromCache, int page, const CMSettings& cmSettings,
                                                  RequestType requestType, int gsRes, bool *realCMYK, bool showMsg)
{
        if (cache.enabled())
        {
                ScColorMgmtEngine engine(cmSettings.doc() ? cmSettings.doc()->colorEngine : ScCore->defaultEngine);
                cache.addModifier("cmEngineID", QString::number(engine.engineID()));
                cache.addModifier("cmEngineDescription", engine.description());
                cache.addModifier("useEmbeddedProfile", QString::number(static_cast<int>(cmSettings.useEmbeddedProfile())));
                cache.addModifier("softProofingAllowed", QString::number(static_cast<int>(cmSettings.softProofingAllowed())));
                cache.addModifier("requestType", QString::number(static_cast<int>(requestType)));
                cache.addModifier("gsRes", QString::number(gsRes));
                cache.addModifier("useColorManagement", QString::number(static_cast<int>(cmSettings.useColorManagement())));
                cache.addModifier("doSoftProofing", QString::number(static_cast<int>(cmSettings.doSoftProofing())));
                cache.addModifier("doGamutCheck", QString::number(static_cast<int>(cmSettings.doGamutCheck())));
                cache.addModifier("useBlackPoint", QString::number(static_cast<int>(cmSettings.useBlackPoint())));
                cache.addModifier("imageRenderingIntent", QString::number(static_cast<int>(cmSettings.imageRenderingIntent())));
                addProfileToCacheModifiers(cache, "monitor", cmSettings.monitorProfile());
                addProfileToCacheModifiers(cache, "printer", cmSettings.printerProfile());

                fromCache = imgInfo.lowResType != 0 && cache.canUseCachedImage() && cache.load(*this) && imgInfo.deserialize(cache);

                if (fromCache)
                {
                        cache.touch();
                        return true;
                }
        }
        else
                fromCache = false;

        return loadPicture(cache.getFilename(), page, cmSettings, requestType, gsRes, realCMYK, showMsg);
}

bool ScImage::saveCache(ScImageCacheProxy & cache)
{
        return cache.enabled() && imgInfo.serialize(cache) && cache.save(*this);
}

bool ScImage::loadPicture(const QString & fn, int page, const CMSettings& cmSettings,
                                                  RequestType requestType, int gsRes, bool *realCMYK, bool showMsg)
{
        // requestType - 0: CMYK, 1: RGB, 3 : RawData, 4: Thumbnail
        // gsRes - is the resolution that ghostscript will render at
        bool isCMYK = false;
        bool ret = false;
//      bool inputProfIsEmbedded = false;
        if (realCMYK != 0)
                *realCMYK = false;
        bool bilevel = false;
//      short resolutionunit = 0;
        RequestType reqType = requestType;
        int cmsFlags = 0;
        int cmsProofFlags = 0;
        auto_ptr<ScImgDataLoader> pDataLoader;
        QFileInfo fi = QFileInfo(fn);
        if (!fi.exists())
                return ret;
        QString ext = fi.suffix().toLower();
        QString profileName = "";
        bool hasEmbeddedProfile = false;
        QList<QByteArray> fmtList = QImageReader::supportedImageFormats();
        QStringList fmtImg;
        ScColorTransform xform;
        ScColorProfile inputProf;
        for (int i = 0; i < fmtList.count(); i++)
        {
                fmtImg.append( QString(fmtList[i].toLower()) );
        }
//      bool found = false;

        // Do some basic checks when requestType is OutputProfile
        if (requestType == OutputProfile)
        {
                if (!cmSettings.useColorManagement())
                        return false;
                ScColorProfile prof = cmSettings.outputProfile();
                if (prof.isNull())
                        return false;
                eColorSpaceType cspace = prof.colorSpace();
                if (cspace != ColorSpace_Rgb && cspace != ColorSpace_Cmyk)
                        return false;
        }
        QString ext2 = getImageType(fn);
        if (ext.isEmpty() || (!ext2.isEmpty() && (ext2 != ext)))
                ext = ext2;
        if (extensionIndicatesPDF(ext))
                pDataLoader.reset( new ScImgDataLoader_PDF() );
        else if (extensionIndicatesEPSorPS(ext))
                pDataLoader.reset( new ScImgDataLoader_PS() );
        else if (extensionIndicatesTIFF(ext))
                pDataLoader.reset( new ScImgDataLoader_TIFF() );
        else if (extensionIndicatesPSD(ext))
        {
                pDataLoader.reset( new ScImgDataLoader_PSD() );
                pDataLoader->setRequest(imgInfo.isRequest, imgInfo.RequestProps);
        }
        else if (extensionIndicatesJPEG(ext))
                pDataLoader.reset( new ScImgDataLoader_JPEG() );
        else if (ext == "pat")
                pDataLoader.reset( new ScImgDataLoader_GIMP() );
        else if (ext == "pgf")
                pDataLoader.reset( new ScImgDataLoader_PGF() );
        else if ((ext == "pct") || (ext == "pic") || (ext == "pict"))
                pDataLoader.reset( new ScImgDataLoader_PICT() );
        else if (ext == "wpg")
                pDataLoader.reset( new ScImgDataLoader_WPG() );
        else if (ext == "ora")
        {
                pDataLoader.reset( new ScImgDataLoader_ORA() );
                pDataLoader->setRequest(imgInfo.isRequest, imgInfo.RequestProps);
        }
#ifdef GMAGICK_FOUND
        else if (fmtImg.contains(ext))
                pDataLoader.reset( new ScImgDataLoader_QT() );
        else
                pDataLoader.reset( new ScImgDataLoader_GMagick() );
#else
        else
                pDataLoader.reset( new ScImgDataLoader_QT() );
#endif

        if (pDataLoader->loadPicture(fn, page, gsRes, (requestType == Thumbnail)))
        {
                QImage::operator=(pDataLoader->image());
                imgInfo = pDataLoader->imageInfoRecord();
                if (requestType == Thumbnail)
                        reqType = RGBData;
        //      if (!cmSettings.useColorManagement() || !useProf)
        //      {
        //              imgInfo.isEmbedded = false;
        //              imgInfo.profileName = "";
        //      }
                if (imgInfo.colorspace == ColorSpaceCMYK)
                {
                        isCMYK = true;
                        if(realCMYK)
                                *realCMYK = true;
                }
                else if (imgInfo.colorspace == ColorSpaceGray)
                        bilevel = true;
                pDataLoader->image() = QImage();
        }
        else
        {
                if      (ScCore->usingGUI() && pDataLoader->issuedErrorMsg() && showMsg)
                {
                        ScMessageBox::critical(ScCore->primaryMainWindow(), CommonStrings::trWarning, pDataLoader->getMessage());
                }
                else if (pDataLoader->issuedErrorMsg())
                {
                        QString msg = pDataLoader->getMessage();
                        qWarning("%s", msg.toLocal8Bit().data() );
                }
                return false;
        }

        if (!(extensionIndicatesPSD(ext) || extensionIndicatesTIFF(ext) || pDataLoader->useRawImage())) //TODO: Unsure about this one!
        {
                if (isNull())
                        return  ret;
        }

        QByteArray embeddedProfile = pDataLoader->embeddedProfile();
        if (cmSettings.useColorManagement())
        {
                if ((embeddedProfile.size() > 0 ) && (cmSettings.useEmbeddedProfile()))
                {
                        inputProf = cmSettings.doc()->colorEngine.openProfileFromMem(embeddedProfile);
                //      inputProfIsEmbedded = true;
                }
                else
                {
                        QString profilePath;
                        //CB If this is null, customfiledialog/picsearch/ScPreview might be sending it
                        Q_ASSERT(cmSettings.doc()!=0);
                        if (isCMYK)
                        {
                                if (ScCore->InputProfilesCMYK.contains(cmSettings.profileName()) && (cmSettings.profileName() != cmSettings.doc()->cmsSettings().DefaultImageCMYKProfile))
                                {
                                        imgInfo.profileName = cmSettings.profileName();
                                //      inputProfIsEmbedded = true;
                                        profilePath = ScCore->InputProfilesCMYK[imgInfo.profileName];
                                        inputProf =  cmSettings.doc()->colorEngine.openProfileFromFile(profilePath);
                                }
                                else
                                {
                                        inputProf = cmSettings.doc()->DocInputImageCMYKProf;
                                        imgInfo.profileName = cmSettings.doc()->cmsSettings().DefaultImageCMYKProfile;
                                //      inputProfIsEmbedded = false;
                                }
                        }
                        else if (bilevel && (reqType == CMYKData))
                                inputProf = NULL; // Workaround to map directly gray to K channel
                        else if (ScCore->InputProfiles.contains(cmSettings.profileName()) && (cmSettings.profileName() != cmSettings.doc()->cmsSettings().DefaultImageRGBProfile))
                        {
                                imgInfo.profileName = cmSettings.profileName();
                                profilePath = ScCore->InputProfiles[imgInfo.profileName];
                        //      inputProfIsEmbedded = true;
                                inputProf = cmSettings.doc()->colorEngine.openProfileFromFile(profilePath);
                        }
                        else
                        {
                                inputProf = cmSettings.doc()->DocInputImageRGBProf;
                                imgInfo.profileName = cmSettings.doc()->cmsSettings().DefaultImageRGBProfile;
                        //      inputProfIsEmbedded = false;
                        }
                }
        }
        else if ((cmSettings.useColorManagement() && embeddedProfile.size() > 0) && (cmSettings.useEmbeddedProfile()))
        {
                inputProf = cmSettings.doc()->colorEngine.openProfileFromMem(embeddedProfile);
        //      inputProfIsEmbedded = true;
        }
        else if (cmSettings.colorManagementAllowed() && isCMYK)
                inputProf = ScCore->defaultCMYKProfile;
        else if (cmSettings.colorManagementAllowed() && bilevel && (reqType == CMYKData))
                inputProf = NULL; // Workaround to map directly gray to K channel
        else if (cmSettings.colorManagementAllowed())
                inputProf = ScCore->defaultRGBProfile;
        ScColorProfile screenProf  = cmSettings.monitorProfile() ? cmSettings.monitorProfile() : ScCore->defaultRGBProfile;
        ScColorProfile printerProf = cmSettings.printerProfile() ? cmSettings.printerProfile() : ScCore->defaultCMYKProfile;
        if (cmSettings.colorManagementAllowed() && inputProf && screenProf && printerProf)
        {
                ScColorMgmtEngine engine(cmSettings.doc() ? cmSettings.doc()->colorEngine : ScCore->defaultEngine);
                eColorFormat inputProfFormat  = pDataLoader->pixelFormat();
                eColorFormat outputProfFormat = Format_YMCK_8;
                eColorSpaceType inputProfColorSpace  = inputProf.colorSpace();
                eColorSpaceType outputProfColorSpace = printerProf.colorSpace();
                if ( inputProfColorSpace == ColorSpace_Gray)
                        inputProfFormat  = Format_GRAY_8; // Grayscale is still a bit tricky
                if ( outputProfColorSpace == ColorSpace_Rgb )
                        outputProfFormat = Format_BGRA_8;
                else if ( outputProfColorSpace == ColorSpace_Cmyk )
                        outputProfFormat = Format_YMCK_8;
                ScColorSpace inputCSpace  = engine.createColorSpace(inputProf, inputProfFormat);
                ScColorSpace screenCSpace = engine.createColorSpace(screenProf, Format_BGRA_8);
                ScColorSpace outputCSpace;
                if (cmSettings.useColorManagement() && cmSettings.doSoftProofing())
                {
                        cmsProofFlags |= Ctf_Softproofing;
                        if (cmSettings.doGamutCheck())
                        {
                                cmsProofFlags |= Ctf_GamutCheck;
                        }
                }
                if (!cmSettings.useColorManagement() || cmSettings.useBlackPoint())
                        cmsFlags |= Ctf_BlackPointCompensation;
                switch (reqType)
                {
                case CMYKData: // CMYK
//                      if ((!isCMYK && (outputProfColorSpace == icSigCmykData)) || (isCMYK && (outputProfColorSpace == icSigRgbData)) )
                                xform = inputCSpace.createTransform(printerProf, outputProfFormat, cmSettings.imageRenderingIntent(), cmsFlags);
                        if (outputProfColorSpace != ColorSpace_Cmyk )
                                *realCMYK = isCMYK = false;
                        outputCSpace = engine.createColorSpace(printerProf, outputProfFormat);
                        break;
                case Thumbnail:
                case RGBData: // RGB
                        if (cmSettings.useColorManagement() && cmSettings.doSoftProofing())
                        {
                                if ((imgInfo.profileName == cmSettings.defaultImageRGBProfile()) || (imgInfo.profileName == cmSettings.defaultImageCMYKProfile()))
                                {
                                        if (isCMYK && (inputProfFormat == Format_CMYK_8))
                                                xform = cmSettings.cmykImageProofingTransform();
                                        else if (inputProfFormat == Format_BGRA_8)
                                                xform = cmSettings.rgbImageProofingTransform();
                                }
                                if (!xform)
                                {
                                        xform = inputCSpace.createProofingTransform(screenCSpace, printerProf,
                                                     cmSettings.intent(), Intent_Relative_Colorimetric, cmsFlags | cmsProofFlags);
                                }
                        }
                        else if (cmSettings.softProofingAllowed() || isCMYK)
                                xform = inputCSpace.createTransform(screenCSpace, cmSettings.intent(), cmsFlags);
                        else
                        {
                                if (pDataLoader->useRawImage())
                                {
                                        QImage::operator=(pDataLoader->r_image.convertToQImage(false));
                                        profileName = imgInfo.profileName;
                                        hasEmbeddedProfile = imgInfo.isEmbedded;
                                        imgInfo = pDataLoader->imageInfoRecord();
                                        imgInfo.profileName = profileName;
                                        imgInfo.isEmbedded = hasEmbeddedProfile;
                                        // JG : this line overwrite image profile info and should not be needed here!!!!
                                        // imgInfo = pDataLoader->imageInfoRecord();
                                }
                        }
                        outputProfColorSpace = ColorSpace_Rgb;
                        outputCSpace = screenCSpace;
                        break;
                case RawData: // no Conversion just raw Data
                        xform = 0;
                        if (pDataLoader->useRawImage())
                        {
                                QImage::operator=(pDataLoader->r_image.convertToQImage(true, true));
                                profileName = imgInfo.profileName;
                                hasEmbeddedProfile = imgInfo.isEmbedded;
                                imgInfo = pDataLoader->imageInfoRecord();
                                imgInfo.profileName = profileName;
                                imgInfo.isEmbedded = hasEmbeddedProfile;
                                // JG : this line overwrite image profile info and should not be needed here!!!!
                                // imgInfo = pDataLoader->imageInfoRecord();
                        }
                        break;
                case OutputProfile: // CMYK
                        ScColorProfile outputProfile = cmSettings.outputProfile();
                        outputProfColorSpace = outputProfile.colorSpace();
                        if ( outputProfColorSpace == ColorSpace_Rgb )
                                outputProfFormat = Format_BGRA_8;
                        else if ( outputProfColorSpace == ColorSpace_Cmyk )
                                outputProfFormat = Format_YMCK_8;
                        xform = inputCSpace.createTransform(outputProfile, outputProfFormat, cmSettings.imageRenderingIntent(), cmsFlags);
                        isCMYK = (outputProfColorSpace == ColorSpace_Cmyk);
                        if (realCMYK)
                                *realCMYK = isCMYK;
                        outputCSpace = engine.createColorSpace(outputProfile, outputProfFormat);
                        break;
                }
                if (xform && outputCSpace)
                {
                        if (pDataLoader->useRawImage())
                        {
                                QImage::operator=(QImage(pDataLoader->r_image.width(), pDataLoader->r_image.height(), QImage::Format_ARGB32));
                                profileName = imgInfo.profileName;
                                hasEmbeddedProfile = imgInfo.isEmbedded;
                                imgInfo = pDataLoader->imageInfoRecord();
                                imgInfo.profileName = profileName;
                                imgInfo.isEmbedded = hasEmbeddedProfile;
                                // JG : this line overwrite image profile info and should not be needed here!!!!
                                // imgInfo = pDataLoader->imageInfoRecord();
                        }
                        uchar* ptr2 = NULL;
                        for (int i = 0; i < height(); i++)
                        {
                                uchar* ptr = scanLine(i);
                                ptr2 = pDataLoader->useRawImage() ? pDataLoader->r_image.scanLine(i) : NULL;
                                if ( inputProfFormat == Format_GRAY_8 && (outputProfColorSpace != ColorSpace_Cmyk) )
                                {
                                        unsigned char* ucs = ptr2 ? (ptr2 + 1) : (ptr + 1);
                                        unsigned char* uc = new unsigned char[width()];
                                        for( int uci = 0; uci < width(); ++uci )
                                        {
                                                uc[uci] = *ucs;
                                                ucs += 4;
                                        }
                                        xform.apply(uc, ptr, width());
                                        delete[] uc;
                                }
                                else if ( inputProfFormat == Format_GRAY_8 && (outputProfColorSpace == ColorSpace_Cmyk) )
                                {
                                        unsigned char  value;
                                        unsigned char* ucs = ptr2 ? ptr2 : ptr;
                                        unsigned char* uc  = ptr;
                                        for( int uci = 0; uci < width(); ++uci, uc += 4 )
                                        {
                                                value = 255 - *(ucs + 1);
                                                uc[0] = uc[1] = uc[2] = 0;
                                                uc[3] = value;
                                                ucs += 4;
                                        }
                                }
                                else
                                {
                                        inputCSpace.convert(outputCSpace, (eRenderIntent) 0, 0, ptr2 ? ptr2 : ptr, ptr, width(), &xform);
                                }
                                if (pDataLoader->useRawImage())
                                {
                                        // This might fix Bug #6328, please test.
                                        /*if (outputProfColorSpace != ColorSpace_Cmyk && bilevel)
                                        {
                                                QRgb alphaFF;
                                                QRgb *p;
                                                p = (QRgb *) ptr;
                                                for (int j = 0; j < width(); j++, p++)
                                                {
                                                        alphaFF = qRgba(0,0,0,ptr2[3]);
                                                        *p |= alphaFF;
                                                        ptr2 += 4;
                                                }
                                        }*/

                                        // FIXME not valid if input or output colorspace are not 8bit / channels
                                        if (inputCSpace.hasAlphaChannel() && outputCSpace.hasAlphaChannel())
                                        {
                                                uint inputAlphaI  = inputCSpace.alphaIndex();
                                                uint outputAlphaI = outputCSpace.alphaIndex();
                                                uint inputBytes   = inputCSpace.bytesPerChannel()  * inputCSpace.numChannels();
                                                uint outputBytes  = outputCSpace.bytesPerChannel() * outputCSpace.numChannels();
                                                uchar* in  = ptr2 + inputAlphaI  * inputCSpace.bytesPerChannel();
                                                uchar* out = ptr  + outputAlphaI * outputCSpace.bytesPerChannel();
                                                for (int j = 0; j < width(); ++j)
                                                {
                                                        *out = *in;
                                                        in  += inputBytes;
                                                        out += outputBytes;
                                                }
                                        }
                                }
                        }
                }
        }
        else
        {
                switch (reqType)
                {
                case CMYKData:
                        if (!isCMYK)
                        {
                                if (extensionIndicatesPSD(ext) || extensionIndicatesTIFF(ext) || pDataLoader->useRawImage())
                                {
                                        QImage::operator=(pDataLoader->r_image.convertToQImage(false));
                                        profileName = imgInfo.profileName;
                                        hasEmbeddedProfile = imgInfo.isEmbedded;
                                        imgInfo = pDataLoader->imageInfoRecord();
                                        imgInfo.profileName = profileName;
                                        imgInfo.isEmbedded = hasEmbeddedProfile;
                                        // JG : this line overwrite image profile info and should not be needed here!!!!
                                        // imgInfo = pDataLoader->imageInfoRecord();
                                }
                                unsigned char cc, cm, cy ,ck;
                                QRgb *ptr;
                                for (int i = 0; i < height(); i++)
                                {
                                        ptr = (QRgb *) scanLine(i);
                                        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);
                                        }
                                }
                        }
                        else
                        {
                                if (extensionIndicatesPSD(ext) || extensionIndicatesTIFF(ext) || pDataLoader->useRawImage())
                                {
                                        QImage::operator=(pDataLoader->r_image.convertToQImage(true, true));
                                        profileName = imgInfo.profileName;
                                        hasEmbeddedProfile = imgInfo.isEmbedded;
                                        imgInfo = pDataLoader->imageInfoRecord();
                                        imgInfo.profileName = profileName;
                                        imgInfo.isEmbedded = hasEmbeddedProfile;
                                        // JG : this line overwrite image profile info and should not be needed here!!!!
                                        // imgInfo = pDataLoader->imageInfoRecord();
                                }
                        }
                        break;
                case RGBData:
                case Thumbnail:
                        if (isCMYK)
                        {
                                if (extensionIndicatesPSD(ext) || extensionIndicatesTIFF(ext) || pDataLoader->useRawImage())
                                {
                                        QImage::operator=(pDataLoader->r_image.convertToQImage(true));
                                        profileName = imgInfo.profileName;
                                        hasEmbeddedProfile = imgInfo.isEmbedded;
                                        imgInfo = pDataLoader->imageInfoRecord();
                                        imgInfo.profileName = profileName;
                                        imgInfo.isEmbedded = hasEmbeddedProfile;
                                        // JG : this line overwrite image profile info and should not be needed here!!!!
                                        // imgInfo = pDataLoader->imageInfoRecord();
                                }
                                else
                                {
                                        unsigned char cr, cg, cb, ck;
                                        QRgb *ptr;
                                        for (int i = 0; i < height(); i++)
                                        {
                                                ptr = (QRgb *) scanLine(i);
                                                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);
                                                }
                                        }
                                }
                        }
                        else
                        {
                                if (extensionIndicatesPSD(ext) || extensionIndicatesTIFF(ext) || pDataLoader->useRawImage())
                                {
                                        QImage::operator=(pDataLoader->r_image.convertToQImage(false));
                                        profileName = imgInfo.profileName;
                                        hasEmbeddedProfile = imgInfo.isEmbedded;
                                        imgInfo = pDataLoader->imageInfoRecord();
                                        imgInfo.profileName = profileName;
                                        imgInfo.isEmbedded = hasEmbeddedProfile;
                                        // JG : this line overwrite image profile info and should not be needed here!!!!
                                        // imgInfo = pDataLoader->imageInfoRecord();
                                }
                        }
                        break;
                case RawData:
                        if (extensionIndicatesPSD(ext) || extensionIndicatesTIFF(ext) || pDataLoader->useRawImage())
                        {
                                QImage::operator=(pDataLoader->r_image.convertToQImage(true, true));
                                profileName = imgInfo.profileName;
                                hasEmbeddedProfile = imgInfo.isEmbedded;
                                imgInfo = pDataLoader->imageInfoRecord();
                                imgInfo.profileName = profileName;
                                imgInfo.isEmbedded = hasEmbeddedProfile;
                                // JG : this line overwrite image profile info and should not be needed here!!!!
                                // imgInfo = pDataLoader->imageInfoRecord();
                        }
                        break;
                default:        // just to silence compiler
                        break;
                }
        }
//      if (((reqType == CMYKData) || ((reqType == RawData) && isCMYK)) && !bilevel)
//              setAlphaBuffer(false);
        setDotsPerMeterX (qMax(2834, (int) (imgInfo.xres / 0.0254)));
        setDotsPerMeterY (qMax(2834, (int) (imgInfo.yres / 0.0254)));
        if (imgInfo.isEmbedded && cmSettings.useEmbeddedProfile())
                imgInfo.isEmbedded = true;
        else
                imgInfo.isEmbedded = false;
        if      (ScCore->usingGUI() && pDataLoader->issuedWarningMsg() && showMsg)
        {
                ScMessageBox::warning(ScCore->primaryMainWindow(), CommonStrings::trWarning, pDataLoader->getMessage());
        }
        else if (pDataLoader->issuedErrorMsg())
        {
                QString msg = pDataLoader->getMessage();
                qWarning("%s", msg.toLocal8Bit().data() );
        }
        return true;
}

bool ScImage::hasSmoothAlpha()
{
        int h = height();
        int w = width();
        QSet<int> alpha;
        QRgb *s, r;
        for( int yi=0; yi < h; ++yi )
        {
                s = (QRgb*)(scanLine( yi ));
                for( int xi=0; xi < w; ++xi )
                {
                        r = *s++;
                        alpha.insert(qAlpha(r));
                        if (alpha.count() > 2)
                                return true;
                }
        }
        return (alpha.count() > 2);
}