Subversion Repositories Scribus

Rev

Rev 24643 | Rev 24646 | 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 <QFile>
#include <QFileInfo>
#include <QObject>
#include <QList>

#include "scconfig.h"
#include "colormgmt/sccolormgmtengine.h"
#include "scimgdataloader_tiff.h"
#include "scribuscore.h"
#include "util_color.h"

static void TagExtender(TIFF *tiff)
{
        static const TIFFFieldInfo xtiffFieldInfo[] =
        {
                { 37724, -3, -3, TIFF_UNDEFINED, FIELD_CUSTOM, true, true,   const_cast<char*>("PhotoshopLayerData") }
        };
        TIFFMergeFieldInfo(tiff, xtiffFieldInfo, sizeof (xtiffFieldInfo) / sizeof (xtiffFieldInfo[0]));
}

ScImgDataLoader_TIFF::ScImgDataLoader_TIFF()
{
        m_photometric = PHOTOMETRIC_MINISBLACK;
        m_samplesperpixel = 72;

        initSupportedFormatList();
}

void ScImgDataLoader_TIFF::initSupportedFormatList()
{
        m_supportedFormats.clear();
        m_supportedFormats.append( "tif" );
        m_supportedFormats.append( "tiff" );
}

void ScImgDataLoader_TIFF::loadEmbeddedProfile(const QString& fn, int /*page*/)
{
        ScColorMgmtEngine engine(ScCore->defaultEngine);
        m_embeddedProfile.resize(0);
        m_profileComponents = 0;
        if (!QFile::exists(fn))
                return;
        TIFFSetTagExtender(TagExtender);
        TIFF* tif = TIFFOpen(fn.toLocal8Bit(), "r");
        if (!tif)
                return;

        uint32_t EmbedLen = 0;
        void*  EmbedBuffer;
        if (TIFFGetField(tif, TIFFTAG_ICCPROFILE, &EmbedLen, &EmbedBuffer))
        {
                QByteArray profArray((const char*) EmbedBuffer, EmbedLen);
                ScColorProfile tiffProf = engine.openProfileFromMem(profArray);
                if (tiffProf)
                {
                        if (tiffProf.colorSpace() == ColorSpace_Rgb)
                                m_profileComponents = 3;
                        if (tiffProf.colorSpace() == ColorSpace_Cmyk)
                                m_profileComponents = 4;
                        if (tiffProf.colorSpace() == ColorSpace_Gray)
                                m_profileComponents = 1;
                        m_embeddedProfile = profArray;
                }
        }
        TIFFClose(tif);
}

bool ScImgDataLoader_TIFF::preloadAlphaChannel(const QString& fn, int page, int res, bool& hasAlpha)
{
        bool success = true;
        bool valid   = m_imageInfoRecord.isRequest;
        QMap<int, ImageLoadRequest> req = m_imageInfoRecord.RequestProps;
        initialize();
        m_imageInfoRecord.RequestProps = req;
        m_imageInfoRecord.isRequest = valid;
        if (!QFile::exists(fn))
                return false;
        hasAlpha = false;
        if (testAlphaChannelAvailability(fn, page, hasAlpha))
        {
                if (!hasAlpha)
                        success = true;
                else if (loadPicture(fn, page, res, false))
                {
                        m_imageInfoRecord.valid = true;
                        hasAlpha = success = true;
                        /*if (photometric == PHOTOMETRIC_SEPARATED)
                        {
                                if (samplesperpixel == 4)
                                        m_imageInfoRecord.valid = hasAlpha = false;
                        }
                        else
                        {
                                if (samplesperpixel == 3)
                                        m_imageInfoRecord.valid = hasAlpha = false;
                        }
                        if (!hasAlpha) r_image.resize(0);*/

                }
        }
        return success;
}

int ScImgDataLoader_TIFF::getLayers(const QString& fn, int /*page*/)
{
        int layerNum = 1;
        int test;
        struct PSDLayer lay;
        TIFFSetTagExtender(TagExtender);
        TIFF* tif = TIFFOpen(fn.toLocal8Bit(), "r");
        if (!tif)
                return layerNum;

        do
        {
                char *layerName = nullptr;
                TIFFGetField(tif, TIFFTAG_PAGENAME, &layerName);
                QString name = QString(layerName);
                if (name.isEmpty())
                        lay.layerName = QString("Layer #%1").arg(layerNum);
                else
                        lay.layerName = name;
                lay.blend = "norm";
                lay.opacity = 255;
                lay.flags = 0;
                m_imageInfoRecord.layerInfo.append(lay);
                m_imageInfoRecord.valid = true;
                layerNum++;
                test = TIFFReadDirectory(tif);
        }
        while (test == 1);
        TIFFClose(tif);

        return layerNum;
}

bool ScImgDataLoader_TIFF::testAlphaChannelAvailability(const QString& fn, int /*page*/, bool& hasAlpha)
{
        int  test;

        QByteArray byteOrder(2, ' ');
        QFile fo(fn);
        if (fo.open(QIODevice::ReadOnly))
        {
                fo.read(byteOrder.data(), 1);
                fo.close();
        }

        TIFFSetTagExtender(TagExtender);
        TIFF* tif = TIFFOpen(fn.toLocal8Bit(), "r");
        if (!tif)
                return false;

        uint16_t extrasamples, *extratypes;
        hasAlpha = false;
        do
        {
                if (TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &extrasamples, &extratypes) == 1)
                {
                        for (int i = 0; i < extrasamples; ++i)
                        {
                                if (extratypes[i] != EXTRASAMPLE_UNSPECIFIED)
                                {
                                        hasAlpha = true;
                                        break;
                                }
                        }
                }
                test = TIFFReadDirectory(tif);
        }
        while (test == 1);

        if (hasAlpha)
        {
                TIFFClose(tif);
                return true;
        }

        unsigned int PhotoshopLen2 = 0;
        unsigned char* PhotoshopBuffer2;
        int gotField = TIFFGetField(tif, 37724, &PhotoshopLen2, &PhotoshopBuffer2);
        if (gotField && (PhotoshopLen2 > 40))
        {
                m_imageInfoRecord.layerInfo.clear();
                QByteArray arrayPhot = QByteArray::fromRawData((const char*)PhotoshopBuffer2, PhotoshopLen2);
                QDataStream s(&arrayPhot, QIODevice::ReadOnly);
                if (byteOrder[0] == 'M')
                        s.setByteOrder(QDataStream::BigEndian);
                else
                        s.setByteOrder(QDataStream::LittleEndian);

                QList<PSDLayer> layerInfo;
                if (loadLayerInfo(s, layerInfo))
                {
                        for (int layer = 0; layer < layerInfo.count(); ++layer)
                        {
                                const PSDLayer& psdLayer = layerInfo.at(layer);
                                uint channel_num = psdLayer.channelLen.count();
                                for (uint channel = 0; channel < channel_num; ++channel)
                                {
                                        if (psdLayer.channelType[channel] == -1)
                                        {
                                                hasAlpha = true;
                                                break;
                                        }
                                }
                                if (hasAlpha) break;
                        }
                }
        }

        TIFFClose(tif);
        return true;
}

void ScImgDataLoader_TIFF::unmultiplyRGBA(RawImage *image)
{
        double r1, g1, b1, coeff;
        unsigned char r, g, b, a;
        for (int i = 0; i < image->height(); ++i)
        {
                unsigned int *ptr = (QRgb *) image->scanLine(i);
                for (int j = 0; j < image->width(); ++j)
                {
                        r = qRed(*ptr);
                        g = qGreen(*ptr);
                        b = qBlue(*ptr);
                        a = qAlpha(*ptr);
                        if (a > 0 && a < 255)
                        {
                                coeff = 255.0 / a;
                                r1 = coeff * r;
                                g1 = coeff * g;
                                b1 = coeff * b;
                                r  = (r1 <= 255.0) ? (unsigned char) r1 : 255;
                                g  = (g1 <= 255.0) ? (unsigned char) g1 : 255;
                                b  = (b1 <= 255.0) ? (unsigned char) b1 : 255;
                                *ptr++ = qRgba(r,g,b,a);
                        }
                        else
                                ++ptr;
                }
        }
}

bool ScImgDataLoader_TIFF::getImageData(TIFF* tif, RawImage *image, uint widtht, uint heightt, uint size, uint16_t photometric, uint16_t bitspersample, uint16_t samplesperpixel, bool &bilevel, bool &isCMYK)
{
        uint32_t *bits = nullptr;
        if (photometric == PHOTOMETRIC_SEPARATED)
        {
                if (samplesperpixel > 5)
                {
                        if (!getImageData_RGBA(tif, image, widtht, heightt, size, bitspersample, samplesperpixel))
                                return false;
                        if (bitspersample == 1)
                                bilevel = true;
                        isCMYK = false;
                        m_pixelFormat = Format_RGBA_8;
                }
                else
                {
                        if (TIFFIsTiled(tif))
                        {
                                uint32_t columns, rows;
                                uint32_t *tile_buf;
                                uint32_t xt, yt;
                                TIFFGetField(tif, TIFFTAG_TILEWIDTH,  &columns);
                                TIFFGetField(tif, TIFFTAG_TILELENGTH, &rows);
                                tile_buf = (uint32_t*) _TIFFmalloc(columns * rows * sizeof(uint32_t));
                                if (tile_buf == nullptr)
                                {
                                        TIFFClose(tif);
                                        return false;
                                }
                                uint32_t tileW = columns, tileH = rows;
                                for (yt = 0; yt < (uint32_t) image->height(); yt += rows)
                                {
                                        if (yt > (uint) image->height())
                                                break;
                                        if (image->height()-yt < rows)
                                                tileH = image->height() - yt;
                                        tileW = columns;
                                        uint32_t yi;
                                        int chans = image->channels();
                                        for (xt = 0; xt < (uint) image->width(); xt += columns)
                                        {
                                                TIFFReadTile(tif, tile_buf, xt, yt, 0, 0);
                                                for (yi = 0; yi < tileH; yi++)
                                                {
                                                        _TIFFmemcpy(image->scanLine(yt + (tileH - 1 - yi)) + xt, tile_buf + tileW * yi, tileW * chans);
                                                }
                                        }
                                }
                                _TIFFfree(tile_buf);
                        }
                        else
                        {
                                tsize_t bytesperrow = TIFFScanlineSize(tif);
                                bits = (uint32_t *) _TIFFmalloc(bytesperrow);
                                int chans = image->channels();
                                if (bits)
                                {
                                        for (unsigned int y = 0; y < heightt; y++)
                                        {
                                                if (TIFFReadScanline(tif, bits, y, 0))
                                                {
                                                        /* The code below allows loading of CMYK TIFFs generated by ImageMagick,
                                                           currently commented out because its an ugly hack atm
                                                           When converting 8-bit PNGs with an alpha channel to CMYK Tiff, ImageMagick
                                                           creates a 16-bit CMYK Tiff !?!??
                                                        if (bitspersample > 8)
                                                        {
                                                                uchar *ptrT = image->scanLine(y);
                                                                uchar *ptrS = (uchar*)bits;
                                                                for (unsigned int x = 0; x < widtht; x++)
                                                                {
                                                                        ptrT[0] = ptrS[1];
                                                                        ptrT[1] = ptrS[3];
                                                                        ptrT[2] = ptrS[5];
                                                                        ptrT[3] = ptrS[7];
                                                                        if (samplesperpixel > 4)
                                                                                ptrT[4] = ptrS[9];
                                                                        ptrT += chans;
                                                                        ptrS += chans * 2;
                                                                }
                                                        }
                                                        else */

                                                                memcpy(image->scanLine(y), bits, chans * widtht);
                                                }
                                        }
                                        _TIFFfree(bits);
                                }
                        }
                        isCMYK = true;
                        m_pixelFormat = (image->channels() == 5) ? Format_CMYKA_8 : Format_CMYK_8;
                }
        }
        else
        {
                if (!getImageData_RGBA(tif, image, widtht, heightt, size, bitspersample, samplesperpixel))
                        return false;
                if (bitspersample == 1)
                        bilevel = true;
                m_pixelFormat = Format_RGBA_8;
        }
        return true;
}

bool ScImgDataLoader_TIFF::getImageData_RGBA(TIFF* tif, RawImage *image, uint widtht, uint heightt, uint size, uint16_t bitspersample, uint16_t samplesperpixel)
{
        uint32_t* bits = (uint32_t *) _TIFFmalloc(size * sizeof(uint32_t));
        if (!bits)
                return false;

        uint16_t  extrasamples(0), *extratypes(nullptr);
        if (!TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &extrasamples, &extratypes))
                extrasamples = 0;
       
        bool gotData = false;
        if (TIFFReadRGBAImage(tif, widtht, heightt, bits, 0))
        {
                for (unsigned int y = 0; y < heightt; y++)
                {
                        memcpy(image->scanLine(heightt - 1 - y), bits + y * widtht, widtht * image->channels());
                        if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
                        {
                                unsigned char *s = image->scanLine( heightt - 1 - y );
                                unsigned char r, g, b, a;
                                for (uint xi = 0; xi < widtht; ++xi)
                                {
                                        r = s[0];
                                        g = s[1];
                                        b = s[2];
                                        a = s[3];
                                        s[0] = a;
                                        s[1] = b;
                                        s[2] = g;
                                        s[3] = r;
                                        s += image->channels();
                                }
                        }
                }
                if (extrasamples > 0 && extratypes[0] == EXTRASAMPLE_ASSOCALPHA)
                        unmultiplyRGBA(image);
                gotData = true;
        }
        _TIFFfree(bits);

        return gotData;
}

void ScImgDataLoader_TIFF::blendOntoTarget(RawImage *tmp, int layOpa, const QString& layBlend, bool cmyk, bool useMask)
{
        if (layBlend == "diss")
        {
                for (int l = 0; l < tmp->height(); l++)
                {
                        srand(m_random_table[ l  % 4096]);
                        for (int k = 0; k < tmp->width(); k++)
                        {
                                int rand_val = rand() & 0xff;
                                if (rand_val > layOpa)
                                        tmp->setAlpha(k, l, 0);
                        }
                }
        }
        int w = r_image.width();
        int h = r_image.height();
        for (int yi=0; yi < h; ++yi)
        {
                unsigned char *s = tmp->scanLine( yi );
                unsigned char *d = r_image.scanLine( yi );
                unsigned char r, g, b, src_r, src_g, src_b, src_a, src_alpha, dst_alpha;
                unsigned char a = 0;
                for (int xi=0; xi < w; ++xi)
                {
                        src_r = s[0];
                        src_g = s[1];
                        src_b = s[2];
                        src_a = s[3];
                        if (r_image.channels() == 5)
                        {
                                dst_alpha = d[4];
                                if (useMask)
                                        src_alpha = s[4];
                                else
                                        src_alpha = 255;
                        }
                        else
                        {
                                if (cmyk)
                                {
                                        dst_alpha = 255;
                                        src_alpha = 255;
                                }
                                else
                                {
                                        dst_alpha = d[3];
                                        src_alpha = s[3];
                                }
                        }
                        if (layBlend != "diss")
                                src_alpha = INT_MULT(src_alpha, layOpa);
                        if ((dst_alpha > 0) && (src_alpha > 0))
                        {
                                if (layBlend == "mul ")
                                {
                                        src_r = INT_MULT(src_r, d[0]);
                                        src_g = INT_MULT(src_g, d[1]);
                                        src_b = INT_MULT(src_b, d[2]);
                                        if (cmyk)
                                                src_a = INT_MULT(src_a, d[3]);
                                }
                                else if (layBlend == "scrn")
                                {
                                        src_r = 255 - ((255-src_r) * (255-d[0]) / 128);
                                        src_g = 255 - ((255-src_g) * (255-d[1]) / 128);
                                        src_b = 255 - ((255-src_b) * (255-d[2]) / 128);
                                        if (cmyk)
                                                src_a = 255 - ((255-src_a) * (255-d[3]) / 128);
                                }
                                else if (layBlend == "over")
                                {
                                        src_r = d[0] < 128 ? src_r * d[0] / 128 : 255 - ((255-src_r) * (255-d[0]) / 128);
                                        src_g = d[1] < 128 ? src_g * d[1] / 128 : 255 - ((255-src_g) * (255-d[1]) / 128);
                                        src_b = d[2] < 128 ? src_b * d[2] / 128 : 255 - ((255-src_b) * (255-d[2]) / 128);
                                        if (cmyk)
                                                src_a = d[3] < 128 ? src_a * d[3] / 128 : 255 - ((255-src_a) * (255-d[3]) / 128);
                                }
                                else if (layBlend == "diff")
                                {
                                        src_r = d[0] > src_r ? d[0] - src_r : src_r - d[0];
                                        src_g = d[1] > src_g ? d[1] - src_g : src_g - d[1];
                                        src_b = d[2] > src_b ? d[2] - src_b : src_b - d[2];
                                        if (cmyk)
                                                src_a = d[3] > src_a ? d[3] - src_a : src_a - d[3];
                                }
                                else if (layBlend == "dark")
                                {
                                        src_r = d[0]  < src_r ? d[0]  : src_r;
                                        src_g = d[1] < src_g ? d[1] : src_g;
                                        src_b = d[2] < src_b ? d[2] : src_b;
                                        if (cmyk)
                                                src_a = d[3] < src_a ? d[3] : src_a;
                                }
                                else if (layBlend == "hLit")
                                {
                                        src_r = src_r < 128 ? src_r * d[0] / 128 : 255 - ((255-src_r) * (255-d[0]) / 128);
                                        src_g = src_g < 128 ? src_g * d[1] / 128 : 255 - ((255-src_g) * (255-d[1]) / 128);
                                        src_b = src_b < 128 ? src_b * d[2] / 128 : 255 - ((255-src_b) * (255-d[2]) / 128);
                                        if (cmyk)
                                                src_a = src_a < 128 ? src_a * d[3] / 128 : 255 - ((255-src_a) * (255-d[3]) / 128);
                                }
                                else if (layBlend == "sLit")
                                {
                                        src_r = src_r * d[0] / 256 + src_r * (255 - ((255-src_r)*(255-d[0]) / 256) - src_r * d[0] / 256) / 256;
                                        src_g = src_g * d[1] / 256 + src_g * (255 - ((255-src_g)*(255-d[1]) / 256) - src_g * d[1] / 256) / 256;
                                        src_b = src_b * d[2] / 256 + src_b * (255 - ((255-src_b)*(255-d[2]) / 256) - src_b * d[2] / 256) / 256;
                                        if (cmyk)
                                                src_a = src_a * d[3] / 256 + src_a * (255 - ((255-src_a)*(255-d[3]) / 256) - src_a * d[3] / 256) / 256;
                                }
                                else if (layBlend == "lite")
                                {
                                        src_r = d[0] < src_r ? src_r : d[0];
                                        src_g = d[1] < src_g ? src_g : d[1];
                                        src_b = d[2] < src_b ? src_b : d[2];
                                        if (cmyk)
                                                src_a = d[3] < src_a ? src_a : d[3];
                                }
                                else if (layBlend == "smud")
                                {
                                        src_r = d[0] + src_r - src_r * d[0] / 128;
                                        src_g = d[1] + src_g - src_g * d[1] / 128;
                                        src_b = d[2] + src_b - src_b * d[2] / 128;
                                        if (cmyk)
                                                src_a = d[3] + src_a - src_a * d[3] / 128;
                                }
                                else if (layBlend == "div ")
                                {
                                        src_r = src_r == 255 ? 255 : ((d[0] * 256) / (255-src_r)) > 255 ? 255 : (d[0] * 256) / (255-src_r);
                                        src_g = src_g == 255 ? 255 : ((d[1] * 256) / (255-src_g)) > 255 ? 255 : (d[1] * 256) / (255-src_g);
                                        src_b = src_b == 255 ? 255 : ((d[2] * 256) / (255-src_b)) > 255 ? 255 : (d[2] * 256) / (255-src_b);
                                        if (cmyk)
                                                src_a = src_a == 255 ? 255 : ((d[3] * 256) / (255-src_a)) > 255 ? 255 : (d[3] * 256) / (255-src_a);
                                }
                                else if (layBlend == "idiv")
                                {
                                        src_r = src_r == 0 ? 0 : (255 - (((255-d[0]) * 256) / src_r)) < 0 ? 0 : 255 - (((255-d[0]) * 256) / src_r);
                                        src_g = src_g == 0 ? 0 : (255 - (((255-d[1]) * 256) / src_g)) < 0 ? 0 : 255 - (((255-d[1]) * 256) / src_g);
                                        src_b = src_b == 0 ? 0 : (255 - (((255-d[2]) * 256) / src_b)) < 0 ? 0 : 255 - (((255-d[2]) * 256) / src_b);
                                        if (cmyk)
                                                src_a = src_a == 0 ? 0 : (255 - (((255-d[3]) * 256) / src_a)) < 0 ? 0 : 255 - (((255-d[3]) * 256) / src_a);
                                }
                                else if (layBlend == "hue ")
                                {
                                        if (!cmyk)
                                        {
                                                uchar new_r = d[0];
                                                uchar new_g = d[1];
                                                uchar new_b = d[2];
                                                RGBTOHSV(src_r, src_g, src_b);
                                                RGBTOHSV(new_r, new_g, new_b);
                                                new_r = src_r;
                                                HSVTORGB(new_r, new_g, new_b);
                                                src_r = new_r;
                                                src_g = new_g;
                                                src_b = new_b;
                                        }
                                }
                                else if (layBlend == "sat ")
                                {
                                        if (!cmyk)
                                        {
                                                uchar new_r = d[0];
                                                uchar new_g = d[1];
                                                uchar new_b = d[2];
                                                RGBTOHSV(src_r, src_g, src_b);
                                                RGBTOHSV(new_r, new_g, new_b);
                                                new_g = src_g;
                                                HSVTORGB(new_r, new_g, new_b);
                                                src_r = new_r;
                                                src_g = new_g;
                                                src_b = new_b;
                                        }
                                }
                                else if (layBlend == "lum ")
                                {
                                        if (!cmyk)
                                        {
                                                uchar new_r = d[0];
                                                uchar new_g = d[1];
                                                uchar new_b = d[2];
                                                RGBTOHSV(src_r, src_g, src_b);
                                                RGBTOHSV(new_r, new_g, new_b);
                                                new_b = src_b;
                                                HSVTORGB(new_r, new_g, new_b);
                                                src_r = new_r;
                                                src_g = new_g;
                                                src_b = new_b;
                                        }
                                }
                                else if (layBlend == "colr")
                                {
                                        if (!cmyk)
                                        {
                                                uchar new_r = d[0];
                                                uchar new_g = d[1];
                                                uchar new_b = d[2];
                                                RGBTOHLS(src_r, src_g, src_b);
                                                RGBTOHLS(new_r, new_g, new_b);
                                                new_r = src_r;
                                                new_b = src_b;
                                                HLSTORGB(new_r, new_g, new_b);
                                                src_r = new_r;
                                                src_g = new_g;
                                                src_b = new_b;
                                        }
                                }
                        }
                        if (dst_alpha == 0)
                        {
                                r = src_r;
                                g = src_g;
                                b = src_b;
                                a = src_a;
                        }
                        else
                        {
                                if (src_alpha > 0)
                                {
                                        r = (d[0] * (255 - src_alpha) + src_r * src_alpha) / 255;
                                        g = (d[1] * (255 - src_alpha) + src_g * src_alpha) / 255;
                                        b = (d[2] * (255 - src_alpha) + src_b * src_alpha) / 255;
                                        if (cmyk)
                                                a = (d[3] * (255 - src_alpha) + src_a * src_alpha) / 255;
                                        if (layBlend != "diss")
                                                src_alpha = dst_alpha + INT_MULT(255 - dst_alpha, src_alpha);
                                }
                        }
                        if (src_alpha > 0)
                        {
                                d[0] = r;
                                d[1] = g;
                                d[2] = b;
                                if (r_image.channels() == 5)
                                {
                                        d[3] = a;
                                        d[4] = src_alpha;
                                }
                                else
                                {
                                        if (cmyk)
                                                d[3] = a;
                                        else
                                                d[3] = src_alpha;
                                }
                        }
                        d += r_image.channels();
                        s += tmp->channels();
                }
        }
}

bool ScImgDataLoader_TIFF::loadPicture(const QString& fn, int page, int res, bool thumbnail)
{
        ScColorMgmtEngine engine(ScCore->defaultEngine);
        bool bilevel = false;
        bool failedPS = false;
        bool foundPS = false;
        short resolutionunit = RESUNIT_INCH; // Default unit is inch
        float xres = 72.0, yres = 72.0;
        if (!QFile::exists(fn))
                return false;
        QByteArray byteOrder(2, ' ');
        QFile fo(fn);
        if (fo.open(QIODevice::ReadOnly))
        {
                fo.read(byteOrder.data(), 1);
                fo.close();
        }
        srand(314159265);
        for (int i = 0; i < 4096; i++)
                m_random_table[i] = rand();
        for (int i = 0; i < 4096; i++)
        {
                int tmp;
                int swap = i + rand() % (4096 - i);
                tmp = m_random_table[i];
                m_random_table[i] = m_random_table[swap];
                m_random_table[swap] = tmp;
        }

        initialize();

        int test;
        bool valid = m_imageInfoRecord.isRequest;
        QMap<int, ImageLoadRequest> req = m_imageInfoRecord.RequestProps;
        m_imageInfoRecord.RequestProps = req;
        m_imageInfoRecord.isRequest = valid;
        m_imageInfoRecord.type = ImageTypeTIF;
        getLayers(fn, page);

        TIFFSetTagExtender(TagExtender);
        TIFF* tif = TIFFOpen(fn.toLocal8Bit(), "r");
        if (!tif)
                return false;

        bool isCMYK = false;
        unsigned int widtht, heightt, size;
        char *description=nullptr, *copyright=nullptr, *datetime=nullptr, *artist=nullptr, *scannerMake=nullptr, *scannerModel=nullptr;
        uint16_t bitspersample, fillorder, planar;

        TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &widtht);
        TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &heightt);
        TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres);
        TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres);
        TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT , &resolutionunit);
        size = widtht * heightt;
        TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &m_photometric);
        TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planar);
        TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitspersample);
        TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &m_samplesperpixel);
        TIFFGetField(tif, TIFFTAG_FILLORDER, &fillorder);

        TIFFGetField(tif, TIFFTAG_MAKE, &scannerMake);
        TIFFGetField(tif, TIFFTAG_MODEL, &scannerModel);
        TIFFGetField(tif, TIFFTAG_IMAGEDESCRIPTION, &description);
        TIFFGetField(tif, TIFFTAG_COPYRIGHT, &copyright);
        TIFFGetField(tif, TIFFTAG_DATETIME, &datetime);
        TIFFGetField(tif, TIFFTAG_ARTIST, &artist);
        m_imageInfoRecord.exifInfo.cameraName = QString(scannerModel);
        m_imageInfoRecord.exifInfo.cameraVendor = QString(scannerMake);
        m_imageInfoRecord.exifInfo.comment = QString(description);
        m_imageInfoRecord.exifInfo.userComment = QString(copyright);
        m_imageInfoRecord.exifInfo.width = widtht;
        m_imageInfoRecord.exifInfo.height = heightt;
        m_imageInfoRecord.exifInfo.dateTime = QString(datetime);
        m_imageInfoRecord.exifInfo.artist = QString(artist);
        m_imageInfoRecord.exifInfo.thumbnail = QImage();
        m_imageInfoRecord.exifDataValid = true;
        uint32_t EmbedLen = 0;
        void*  EmbedBuffer;
        if (TIFFGetField(tif, TIFFTAG_ICCPROFILE, &EmbedLen, &EmbedBuffer))
        {
                QByteArray profArray((const char*) EmbedBuffer, EmbedLen);
                ScColorProfile tiffProf = engine.openProfileFromMem(profArray);
                m_embeddedProfile = profArray;
                m_imageInfoRecord.profileName = tiffProf.productDescription();
                m_imageInfoRecord.embeddedProfileName = m_imageInfoRecord.profileName;
                m_imageInfoRecord.isEmbedded = true;
        }
        else
        {
                m_imageInfoRecord.isEmbedded = false;
                m_imageInfoRecord.profileName.clear();
                m_imageInfoRecord.embeddedProfileName.clear();
        }
        unsigned int PhotoshopLen = 0;
        unsigned char* PhotoshopBuffer;
        if (TIFFGetField(tif, TIFFTAG_PHOTOSHOP, &PhotoshopLen, &PhotoshopBuffer) )
        {
                if (PhotoshopLen != 0)
                {
                        QByteArray arrayPhot = QByteArray((const char*)PhotoshopBuffer,PhotoshopLen);
                        QDataStream strPhot(&arrayPhot,QIODevice::ReadOnly);
                        strPhot.setByteOrder( QDataStream::BigEndian );
                        PSDHeader fakeHeader;
                        fakeHeader.width = widtht;
                        fakeHeader.height = heightt;
                        parseResourceData(strPhot, fakeHeader, PhotoshopLen);
                        m_imageInfoRecord.exifInfo.width = widtht;
                        m_imageInfoRecord.exifInfo.height = heightt;
                        if (!m_imageInfoRecord.valid)
                                m_imageInfoRecord.valid = (m_imageInfoRecord.PDSpathData.size())>0;
                        if (thumbnail)
                        {
                                if (m_photometric == PHOTOMETRIC_SEPARATED)
                                {
                                        isCMYK = true;
                                        m_imageInfoRecord.colorspace = ColorSpaceCMYK;
                                }
                                else if (m_samplesperpixel == 1)
                                {
                                        m_imageInfoRecord.colorspace = ColorSpaceGray;
                                        isCMYK = false;
                                }
                                else
                                        m_imageInfoRecord.colorspace = ColorSpaceRGB;
                                if (bitspersample == 1)
                                        bilevel = true;
                                if (!m_imageInfoRecord.exifInfo.thumbnail.isNull())
                                {
                                        if (isCMYK)
                                        {
                                                r_image.create(m_imageInfoRecord.exifInfo.thumbnail.width(), m_imageInfoRecord.exifInfo.thumbnail.height(), 5);
                                                m_pixelFormat = Format_CMYKA_8;;
                                        }
                                        else
                                        {
                                                r_image.create(m_imageInfoRecord.exifInfo.thumbnail.width(), m_imageInfoRecord.exifInfo.thumbnail.height(), 4);
                                                m_pixelFormat = (m_imageInfoRecord.colorspace == ColorSpaceCMYK) ? Format_CMYK_8 : Format_RGBA_8;
                                        }
                                        r_image.fill(0);
                                        QRgb *s;
                                        uchar *d;
                                        unsigned char cc, cm, cy, ck;
                                        for (int yit = 0; yit < m_imageInfoRecord.exifInfo.thumbnail.height(); ++yit)
                                        {
                                                s = (QRgb*)(m_imageInfoRecord.exifInfo.thumbnail.scanLine(yit));
                                                d = r_image.scanLine(yit);
                                                for (int xit = 0; xit < m_imageInfoRecord.exifInfo.thumbnail.width(); ++xit)
                                                {
                                                        if (isCMYK)
                                                        {
                                                                cc = 255 - qRed(*s);
                                                                cm = 255 - qGreen(*s);
                                                                cy = 255 - qBlue(*s);
                                                                ck = qMin(qMin(cc, cm), cy);
                                                                d[0] = cc-ck;
                                                                d[1] = cm-ck;
                                                                d[2] = cy-ck;
                                                                d[3] = ck;
                                                                d[4] = 255;
                                                        }
                                                        else
                                                        {
                                                                d[0] = qRed(*s);
                                                                d[1] = qGreen(*s);
                                                                d[2] = qBlue(*s);
                                                                d[3] = 255;
                                                        }
                                                        s++;
                                                        d += r_image.channels();
                                                }
                                        }
                                        TIFFClose(tif);
                                        return true;
                                }
                        }
                }
        }

        unsigned int PhotoshopLen2 = 0;
        unsigned char* PhotoshopBuffer2;
        int gotField = TIFFGetField(tif, 37724, &PhotoshopLen2, &PhotoshopBuffer2);
        if (gotField && (PhotoshopLen2 > 40))
        {
                m_imageInfoRecord.layerInfo.clear();
                QByteArray arrayPhot = QByteArray::fromRawData((const char*)PhotoshopBuffer2, PhotoshopLen2);
                QDataStream s(&arrayPhot,QIODevice::ReadOnly);
                if (byteOrder[0] == 'M')
                        s.setByteOrder( QDataStream::BigEndian );
                else
                        s.setByteOrder( QDataStream::LittleEndian );

                failedPS = !loadLayerInfo(s, m_imageInfoRecord.layerInfo);
                if (!failedPS)
                {
                        int chans = 4;
                        int numChannels = m_imageInfoRecord.layerInfo.last().channelType.count();
                        int numLayers = m_imageInfoRecord.layerInfo.count();
                        PSDHeader fakeHeader;
                        fakeHeader.width = widtht;
                        fakeHeader.height = heightt;
                        fakeHeader.channel_count = numChannels;
                        fakeHeader.depth = 8;
                        if (m_photometric == PHOTOMETRIC_SEPARATED)
                        {
                                isCMYK = true;
                                fakeHeader.color_mode = CM_CMYK;
                                chans = 5;
                        }
                        else if (m_samplesperpixel == 1)
                        {
                                fakeHeader.color_mode = CM_GRAYSCALE;
                                isCMYK = false;
                                chans = 4;
                        }
                        else
                        {
                                fakeHeader.color_mode = CM_RGB;
                                isCMYK = false;
                                chans = 4;
                        }
                        if (!r_image.create(widtht, heightt, chans))
                                return false;
                        r_image.fill(0);
                        bool firstLayer = true;
                        for (int layer = 0; layer < numLayers; layer++)
                        {
                                loadLayerChannels( s, fakeHeader, m_imageInfoRecord.layerInfo, layer, &firstLayer );
                        }
                        arrayPhot.clear();
                        TIFFClose(tif);
                        foundPS = true;
                        if (m_imageInfoRecord.layerInfo.count() == 1)
                                m_imageInfoRecord.layerInfo.clear();
                }
                else
                {
                        arrayPhot.clear();
                        getLayers(fn, page);
                }
        }

        if (xres <= 1.0 || yres <= 1.0)
        {
                xres = yres = 72.0;
                QFileInfo qfi(fn);
                m_message = QObject::tr("%1 may be corrupted : missing resolution tags").arg(qfi.fileName());
                m_msgType = warningMsg;
        }
        if ((!foundPS) || (failedPS))
        {
                int chans = 4;
                if (m_photometric == PHOTOMETRIC_SEPARATED)
                {
                        if (m_samplesperpixel > 5)
                                chans = 4;
                        else
                                chans = m_samplesperpixel;
                }
                else
                        chans = 4;
                if (!r_image.create(widtht, heightt, chans))
                {
                        TIFFClose(tif);
                        return false;
                }
                r_image.fill(0);
                int layerNum = 0;
                do
                {
                        RawImage tmpImg;
                        if (!tmpImg.create(widtht, heightt, chans))
                        {
                                TIFFClose(tif);
                                return false;
                        }

                        tmpImg.fill(0);
                        if (!getImageData(tif, &tmpImg, widtht, heightt, size, m_photometric, bitspersample, m_samplesperpixel, bilevel, isCMYK))
                        {
                                TIFFClose(tif);
                                return false;
                        }
                        bool visible = true;
                        bool useMask = true;
                        if ((m_imageInfoRecord.isRequest) && (m_imageInfoRecord.RequestProps.contains(layerNum)))
                                visible = m_imageInfoRecord.RequestProps[layerNum].visible;
                        QString layBlend("norm");
                        if ((m_imageInfoRecord.isRequest) && (m_imageInfoRecord.RequestProps.contains(layerNum)))
                                layBlend = m_imageInfoRecord.RequestProps[layerNum].blend;
                        if ((m_imageInfoRecord.isRequest) && (m_imageInfoRecord.RequestProps.contains(layerNum)))
                                useMask = m_imageInfoRecord.RequestProps[layerNum].useMask;
                        int layOpa = 255;
                        if ((m_imageInfoRecord.isRequest) && (m_imageInfoRecord.RequestProps.contains(layerNum)))
                                layOpa = m_imageInfoRecord.RequestProps[layerNum].opacity;
                        if (visible)
                        {
                                if ((layerNum == 0) && (layBlend != "diss"))
                                        r_image = tmpImg;
                                else
                                        blendOntoTarget(&tmpImg, layOpa, layBlend, isCMYK, useMask);
                        }
                        //JG Copy should not be necessary as QImage is implicitly shared in Qt4
                        QImage imt; //QImage imt = tmpImg.copy();
                        double sx = tmpImg.width() / 40.0;
                        double sy = tmpImg.height() / 40.0;
                        imt = tmpImg.convertToQImage(chans > 4);
                        imt = sy < sx ? imt.scaled(qRound(imt.width() / sx), qRound(imt.height() / sx), Qt::IgnoreAspectRatio, Qt::SmoothTransformation) :
                                                                                imt.scaled(qRound(imt.width() / sy), qRound(imt.height() / sy), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
                        m_imageInfoRecord.layerInfo[layerNum].thumb = imt.copy();
                        if (chans > 4)
                        {
                                QImage imt2 = imt.createAlphaMask();
                                imt2.invertPixels();
                                m_imageInfoRecord.layerInfo[layerNum].thumb_mask = imt2.copy();
                        }
                        else
                                m_imageInfoRecord.layerInfo[layerNum].thumb_mask = QImage();
                        layerNum++;

                        if ((m_imageInfoRecord.layerInfo.count() == 1) && (chans < 5))
                                m_imageInfoRecord.layerInfo.clear();
                        test = TIFFReadDirectory(tif);

                        // #10415 : check that image size for the current directory is the same as the main one
                        // Some progs use tiff directory to store previews, we do not handle such case
                        // and may perform reads in non allocated blocks if we continue
                        while (test == 1)
                        {
                                unsigned int dirWidth(0), dirHeight(0);
                                TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &dirWidth);
                                TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &dirHeight);
                                if (dirWidth == widtht && dirHeight == heightt)
                                        break;
                                test = TIFFReadDirectory(tif);
                                layerNum++;
                        }
                }
                while (test == 1);
                TIFFClose(tif);
        }
        if (resolutionunit == RESUNIT_INCH)
        {
                m_image.setDotsPerMeterX ((int) (xres / 0.0254));
                m_image.setDotsPerMeterY ((int) (yres / 0.0254));
                m_imageInfoRecord.xres = qRound(xres);
                m_imageInfoRecord.yres = qRound(yres);
        }
        else if (resolutionunit == RESUNIT_CENTIMETER)
        {
                m_image.setDotsPerMeterX ((int) (xres * 100.0));
                m_image.setDotsPerMeterY ((int) (yres * 100.0));
                m_imageInfoRecord.xres = qRound(xres*2.54);
                m_imageInfoRecord.yres = qRound(yres*2.54);
        }
        if (isCMYK)
        {
                m_imageInfoRecord.colorspace = ColorSpaceCMYK;
                m_pixelFormat = (r_image.channels() == 5) ? Format_CMYKA_8 : Format_CMYK_8;
        }
        else if (m_samplesperpixel == 1)
        {
                // Do not set m_pixelFormat here as the real pixel format is most probably different than gray
                if (bitspersample == 1)
                        m_imageInfoRecord.colorspace = ColorSpaceMonochrome;
                else
                        m_imageInfoRecord.colorspace = ColorSpaceGray;
        }
        else
        {
                m_imageInfoRecord.colorspace = ColorSpaceRGB;
                m_pixelFormat = Format_RGBA_8;
        }
        m_imageInfoRecord.BBoxX = 0;
        m_imageInfoRecord.BBoxH = r_image.height();
        m_imageInfoRecord.valid = !(m_imageInfoRecord.layerInfo.isEmpty() && m_imageInfoRecord.PDSpathData.isEmpty());
        return true;
}

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

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

bool ScImgDataLoader_TIFF::loadLayerInfo(QDataStream & s, QList<PSDLayer> &layerInfo)
{
        bool failedPS = false;
        uint addRes, layerinfo, channelLen, signature, extradata, layermasksize, layerRange, dummy;
        int top, left, bottom, right;
        short numLayers, numChannels;
        short channelType;
        uchar blendKey[4];
        uchar opacity, clipping, flags, filler;
        QString blend;

        layerInfo.clear();

        struct PSDLayer lay;
        do
        {
                if (s.atEnd())
                {
                        layerInfo.clear();
                        failedPS = true;
                        break;
                }
                s >> signature;
        }
        while (signature != 0x4c617972);

        if (failedPS)
                return false;

        s >> layerinfo;
        s >> numLayers;
        if (numLayers < 0)
                numLayers = -numLayers;
        if (numLayers == 0)
                return true;

        QDataStream::ByteOrder byteOrder = s.byteOrder();

        for (int layer = 0; layer < numLayers; layer++)
        {
                s >> top;
                lay.ypos = top;
                s >> left;
                lay.xpos = left;
                s >> bottom;
                lay.height = bottom - top;
                s >> right;
                lay.width = right - left;
                s >> numChannels;
                if (numChannels > 6)    // we don't support images with more than 6 channels yet
                {
                        m_imageInfoRecord.layerInfo.clear();
                        failedPS = true;
                        break;
                }
                lay.channelType.clear();
                lay.channelLen.clear();
                for (int channels = 0; channels < numChannels; channels++)
                {
                        s >> channelType;
                        s >> channelLen;
                        lay.channelType.append(channelType);
                        lay.channelLen.append(channelLen);
                }
                s >> signature;
                blend = "";
                for (int i = 0; i < 4; i++)
                {
                        s >> blendKey[i];
                        if (byteOrder == QDataStream::BigEndian)
                                blend.append(QChar(blendKey[i]));
                        else
                                blend.prepend(QChar(blendKey[i]));
                }
                lay.blend = blend;
                s >> opacity;
                lay.opacity = opacity;
                s >> clipping;
                lay.clipping = clipping;
                s >> flags;
                if (flags & 8)
                {
                        if (flags & 16) // Unknown combination of layer flags, probably an adjustment or effects layer
                        {
                                layerInfo.clear();
                                failedPS = true;
                                break;
                        }
                }
                lay.flags = flags;
                s >> filler;
                s >> extradata;
                s >> layermasksize;
                lay.maskYpos = 0;
                lay.maskXpos = 0;
                lay.maskHeight = 0;
                lay.maskWidth = 0;
                if (layermasksize != 0)
                {
                        s >> lay.maskYpos;
                        s >> lay.maskXpos;
                        s >> dummy;
                        lay.maskHeight = dummy - lay.maskYpos;
                        s >> dummy;
                        lay.maskWidth = dummy - lay.maskXpos;
                        s >> dummy;
                }
                s >> layerRange;
                s.device()->seek( s.device()->pos() + layerRange );
                lay.layerName = getLayerString(s);
                layerInfo.append(lay);
                s >> signature;
                if (signature == 0x3842494D)
                {
                        while (signature == 0x3842494D)
                        {
                                s >> signature;
                                s >> addRes;
                                s.device()->seek( s.device()->pos() + addRes );
                                s >> signature;
                        }
                        s.device()->seek( s.device()->pos() - 4 );
                }
                else
                {
                        s.device()->seek( s.device()->pos() - 2 );
                        s >> signature;
                        if (signature == 0x3842494D)
                        {
                                while (signature == 0x3842494D)
                                {
                                        s >> signature;
                                        s >> addRes;
                                        s.device()->seek( s.device()->pos() + addRes );
                                        s >> signature;
                                }
                                s.device()->seek( s.device()->pos() - 4 );
                        }
                        else
                                s.device()->seek( s.device()->pos() - 6 );
                }
        }

        return (!failedPS && (layerInfo.count() >= 0));
}

bool ScImgDataLoader_TIFF::loadLayerChannels( QDataStream & s, const PSDHeader & header, QList<PSDLayer> &layerInfo, uint layer, bool* firstLayer)
{
        // Find out if the data is compressed.
        // Known values:
        //   0: no compression
        //   1: RLE compressed
        qint64 base = s.device()->pos();
        qint64 base2 = base;
        uint channel_num = layerInfo[layer].channelLen.count();
        bool hasMask = false;
        bool hasAlpha = false;
        RawImage r2_image;
        RawImage mask;
        bool createOk = false;
        if (header.color_mode == CM_CMYK)
        {
                createOk = r2_image.create(layerInfo[layer].width, layerInfo[layer].height, qMax(channel_num, (uint) 5));
                r2_image.fill(0);
        }
        else
        {
                createOk = r2_image.create(layerInfo[layer].width, layerInfo[layer].height, qMax(channel_num, (uint) 4));
                r2_image.fill(0);
        }
        if (!createOk)
        {
                for (uint channel = 0; channel < channel_num; channel++)
                {
                        base2 += layerInfo[layer].channelLen[channel];
                }
                s.device()->seek( base2 );
                return false;
        }
        channel_num = qMin(channel_num, (uint) 39);
        uint components[40] = {0};
        for (uint channel = 0; channel < channel_num; channel++)
        {
                switch (layerInfo[layer].channelType[channel])
                {
                case 0:
                        components[channel] = 0;
                        break;
                case 1:
                        components[channel] = 1;
                        break;
                case 2:
                        components[channel] = 2;
                        break;
                case 3:
                        components[channel] = 3;
                        break;
                case -1:
                        if (header.color_mode == CM_CMYK)
                        {
                                if (channel_num == 6)
                                        components[channel] = channel_num - 2;
                                else
                                        components[channel] = channel_num - 1;
                        }
                        else
                        {
                                if (channel_num == 5)
                                        components[channel] = channel_num - 2;
                                else
                                {
                                        if (header.color_mode == CM_GRAYSCALE)
                                                components[channel] = 3;
                                        else
                                                components[channel] = channel_num - 1;
                                }
                        }
                        hasAlpha = true;
                        break;
                case -2:
                        components[channel] = channel_num-1;
                        break;
                }
        }
        if (!hasAlpha)
                r2_image.fill((char) 255);
        for (uint channel = 0; channel < channel_num; channel++)
        {
                if (layerInfo[layer].channelType[channel] == -2)
                {
                        if (!mask.create( layerInfo[layer].maskWidth, layerInfo[layer].maskHeight, 1))
                                break;
                        mask.fill(0);
                        if (!loadChannel(s, header, layerInfo, layer, channel, 0, mask))
                                break;
                        hasMask = true;
                }
                if (!loadChannel(s, header, layerInfo, layer, channel, components[channel], r2_image))
                        break;
        }
        for (uint channel = 0; channel < channel_num; channel++)
        {
                base2 += layerInfo[layer].channelLen[channel];
        }
        s.device()->seek( base2 );
        QImage tmpImg2;
        if (header.color_mode == CM_CMYK)
                tmpImg2 = r2_image.convertToQImage(true);
        else
                tmpImg2 = r2_image.convertToQImage(false);
        QImage imt;
        double sx = tmpImg2.width() / 40.0;
        double sy = tmpImg2.height() / 40.0;
        imt = sy < sx ?  tmpImg2.scaled(qRound(tmpImg2.width() / sx), qRound(tmpImg2.height() / sx), Qt::IgnoreAspectRatio, Qt::SmoothTransformation) :
              tmpImg2.scaled(qRound(tmpImg2.width() / sy), qRound(tmpImg2.height() / sy), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
        layerInfo[layer].thumb = imt.copy();
        if (hasMask)
        {
                QImage imt2;
                QImage tmpImg;
                tmpImg = mask.convertToQImage(true);
                double sx = tmpImg.width() / 40.0;
                double sy = tmpImg.height() / 40.0;
                imt2 = sy < sx ?  tmpImg.scaled(qRound(tmpImg.width() / sx), qRound(tmpImg.height() / sx), Qt::IgnoreAspectRatio, Qt::SmoothTransformation) :
              tmpImg.scaled(qRound(tmpImg.width() / sy), qRound(tmpImg.height() / sy), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
                imt2.invertPixels();
                layerInfo[layer].thumb_mask = imt2.copy();
        }
        else
                layerInfo[layer].thumb_mask = QImage();
        if (!m_imageInfoRecord.isRequest || !m_imageInfoRecord.RequestProps.contains(layer))
                m_imageInfoRecord.RequestProps[layer].useMask = true;
        bool visible = !(layerInfo[layer].flags & 2);
        if ((m_imageInfoRecord.isRequest) && (m_imageInfoRecord.RequestProps.contains(layer)))
                visible = m_imageInfoRecord.RequestProps[layer].visible;
        if (visible)
        {
                unsigned int startSrcY, startSrcX, startDstY, startDstX;
                if (layerInfo[layer].ypos < 0)
                {
                        startSrcY = abs(layerInfo[layer].ypos);
                        startDstY = 0;
                }
                else
                {
                        startSrcY = 0;
                        startDstY = layerInfo[layer].ypos;
                }
                if (layerInfo[layer].xpos < 0)
                {
                        startSrcX = abs(layerInfo[layer].xpos);
                        startDstX = 0;
                }
                else
                {
                        startSrcX = 0;
                        startDstX = layerInfo[layer].xpos;
                }
                unsigned int startSrcXm; //, startSrcYm, startDstYm, startDstXm;
        /*      if (layerInfo[layer].maskYpos < 0)
                {
                        startSrcYm = abs(layerInfo[layer].maskYpos);
                        startDstYm = 0;
                }
                else
                {
                        startSrcYm = 0;
                        startDstYm = layerInfo[layer].maskYpos;
                }*/

                if (layerInfo[layer].maskXpos < 0)
                {
                        startSrcXm = abs(layerInfo[layer].maskXpos);
                //      startDstXm = 0;
                }
                else
                {
                        startSrcXm = 0;
                //      startDstXm = layerInfo[layer].maskXpos;
                }
                QString layBlend2 = layerInfo[layer].blend;
                if ((m_imageInfoRecord.isRequest) && (m_imageInfoRecord.RequestProps.contains(layer)))
                        layBlend2 = m_imageInfoRecord.RequestProps[layer].blend;
                if (layBlend2 == "diss")
                {
                        hasAlpha = true;
                        int layOpa = layerInfo[layer].opacity;
                        if ((m_imageInfoRecord.isRequest) && (m_imageInfoRecord.RequestProps.contains(layer)))
                                layOpa = m_imageInfoRecord.RequestProps[layer].opacity;
                        for (int l = 0; l < r2_image.height(); l++)
                        {
                                srand(m_random_table[ l  % 4096]);
                                for (int k = 0; k < r2_image.width(); k++)
                                {
                                        int rand_val = rand() & 0xff;
                                        if (rand_val > layOpa)
                                                r2_image.setAlpha(k, l, 0);
                                }
                        }
                }
                if (*firstLayer)
                {
                        for (int yi=static_cast<int>(startSrcY); yi < qMin(r2_image.height(),  r_image.height()); ++yi)
                        {
                                unsigned char *s = r2_image.scanLine( yi );
                                unsigned char *d = r_image.scanLine( qMin(static_cast<int>(startDstY),  r_image.height()-1) );
                                d += qMin(static_cast<int>(startDstX), r_image.width()-1) * r_image.channels();
                                s += qMin(static_cast<int>(startSrcX), r2_image.width()-1) * r2_image.channels();
                                for (int xi=static_cast<int>(startSrcX); xi < qMin(r2_image.width(),  r_image.width()); ++xi )
                                {
                                        d[0] = s[0];
                                        d[1] = s[1];
                                        d[2] = s[2];
                                        if (header.color_mode == CM_RGB)
                                        {
                                                if (hasAlpha)
                                                        d[3] = s[3];
                                                else
                                                        d[3] = 255;
                                        }
                                        else
                                        {
                                                d[3] = s[3];
                                                if (hasAlpha)
                                                        d[4] = s[4];
                                                else
                                                        d[4] = 255;
                                        }
                                        s += r2_image.channels();
                                        d += r_image.channels();
                                }
                                startDstY++;
                        }
                }
                else
                {
                        for (int i = static_cast<int>(startSrcY); i < layerInfo[layer].height; i++)
                        {
                                unsigned char *d = r_image.scanLine(qMin(static_cast<int>(startDstY),  r_image.height()-1));
                                unsigned char *s = r2_image.scanLine(qMin(i, r2_image.height()-1));
                                d += qMin(static_cast<int>(startDstX),  r_image.width()-1) * r_image.channels();
                                s += qMin(static_cast<int>(startSrcX), r2_image.width()-1) * r2_image.channels();
                                unsigned char *sm = nullptr;
                                if (hasMask)
                                {
                                        sm = mask.scanLine(qMin(i, mask.height()-1));
                                        sm += qMin(static_cast<int>(startSrcXm), mask.width()-1) * mask.channels();
                                }
                                startDstY++;
                                unsigned char r, g, b, src_r, src_g, src_b, src_a, src_alpha, dst_alpha;
                                unsigned char a = 0;
                                unsigned int maxDestX = r_image.width() - startDstX + startSrcX - 1;
                                for (unsigned int j = startSrcX; j < qMin(maxDestX, static_cast<unsigned int>(layerInfo[layer].width)); j++)
                                {
                                        src_r = s[0];
                                        src_g = s[1];
                                        src_b = s[2];
                                        src_a = s[3];
                                        if (hasAlpha)
                                        {
                                                if (hasMask)
                                                {
                                                        if (m_imageInfoRecord.RequestProps[layer].useMask)
                                                                src_alpha = sm[0];
                                                        else
                                                                src_alpha = s[channel_num - 2];
                                                }
                                                else
                                                        src_alpha = s[channel_num - 1];
                                        }
                                        else
                                                src_alpha = 255;
                                        if ((hasMask) && (m_imageInfoRecord.RequestProps[layer].useMask))
                                                src_alpha = sm[0];
                                        int layOpa = layerInfo[layer].opacity;
                                        if ((m_imageInfoRecord.isRequest) && (m_imageInfoRecord.RequestProps.contains(layer)))
                                                layOpa = m_imageInfoRecord.RequestProps[layer].opacity;
                                        QString layBlend = layerInfo[layer].blend;
                                        if ((m_imageInfoRecord.isRequest) && (m_imageInfoRecord.RequestProps.contains(layer)))
                                                layBlend = m_imageInfoRecord.RequestProps[layer].blend;
                                        if (layBlend != "diss")
                                                src_alpha = INT_MULT(src_alpha, layOpa);
                                        if (header.color_mode == CM_CMYK)
                                                dst_alpha = d[4];
                                        else
                                                dst_alpha = d[3];
                                        if ((dst_alpha > 0) && (src_alpha > 0))
                                        {
                                                if (layBlend == "mul ")
                                                {
                                                        src_r = INT_MULT(src_r, d[0]);
                                                        src_g = INT_MULT(src_g, d[1]);
                                                        src_b = INT_MULT(src_b, d[2]);
                                                        if (header.color_mode == CM_CMYK)
                                                                src_a = INT_MULT(src_a, d[3]);
                                                }
                                                else if (layBlend == "scrn")
                                                {
                                                        src_r = 255 - ((255-src_r) * (255-d[0]) / 128);
                                                        src_g = 255 - ((255-src_g) * (255-d[1]) / 128);
                                                        src_b = 255 - ((255-src_b) * (255-d[2]) / 128);
                                                        if (header.color_mode == CM_CMYK)
                                                                src_a = 255 - ((255-src_a) * (255-d[3]) / 128);
                                                }
                                                else if (layBlend == "over")
                                                {
                                                        src_g = d[1] < 128 ? src_g * d[1] / 128 : 255 - ((255-src_g) * (255-d[1]) / 128);
                                                        src_b = d[2] < 128 ? src_b * d[2] / 128 : 255 - ((255-src_b) * (255-d[2]) / 128);
                                                        src_a = d[3] < 128 ? src_a * d[3] / 128 : 255 - ((255-src_a) * (255-d[3]) / 128);
                                                        if (header.color_mode == CM_CMYK)
                                                                src_r = d[0] < 128 ? src_r * d[0] / 128 : 255 - ((255-src_r) * (255-d[0]) / 128);
                                                }
                                                else if (layBlend == "diff")
                                                {
                                                        src_r = d[0] > src_r ? d[0] - src_r : src_r - d[0];
                                                        src_g = d[1] > src_g ? d[1] - src_g : src_g - d[1];
                                                        src_b = d[2] > src_b ? d[2] - src_b : src_b - d[2];
                                                        if (header.color_mode == CM_CMYK)
                                                                src_a = d[3] > src_a ? d[3] - src_a : src_a - d[3];
                                                }
                                                else if (layBlend == "dark")
                                                {
                                                        src_r = d[0]  < src_r ? d[0]  : src_r;
                                                        src_g = d[1] < src_g ? d[1] : src_g;
                                                        src_b = d[2] < src_b ? d[2] : src_b;
                                                        if (header.color_mode == CM_CMYK)
                                                                src_a = d[3] < src_a ? d[3] : src_a;
                                                }
                                                else if (layBlend == "hLit")
                                                {
                                                        src_r = src_r < 128 ? src_r * d[0] / 128 : 255 - ((255-src_r) * (255-d[0]) / 128);
                                                        src_g = src_g < 128 ? src_g * d[1] / 128 : 255 - ((255-src_g) * (255-d[1]) / 128);
                                                        src_b = src_b < 128 ? src_b * d[2] / 128 : 255 - ((255-src_b) * (255-d[2]) / 128);
                                                        if (header.color_mode == CM_CMYK)
                                                                src_a = src_a < 128 ? src_a * d[3] / 128 : 255 - ((255-src_a) * (255-d[3]) / 128);
                                                }
                                                else if (layBlend == "sLit")
                                                {
                                                        src_r = src_r * d[0] / 256 + src_r * (255 - ((255-src_r)*(255-d[0]) / 256) - src_r * d[0] / 256) / 256;
                                                        src_g = src_g * d[1] / 256 + src_g * (255 - ((255-src_g)*(255-d[1]) / 256) - src_g * d[1] / 256) / 256;
                                                        src_b = src_b * d[2] / 256 + src_b * (255 - ((255-src_b)*(255-d[2]) / 256) - src_b * d[2] / 256) / 256;
                                                        if (header.color_mode == CM_CMYK)
                                                                src_a = src_a * d[3] / 256 + src_a * (255 - ((255-src_a)*(255-d[3]) / 256) - src_a * d[3] / 256) / 256;
                                                }
                                                else if (layBlend == "lite")
                                                {
                                                        src_r = d[0] < src_r ? src_r : d[0];
                                                        src_g = d[1] < src_g ? src_g : d[1];
                                                        src_b = d[2] < src_b ? src_b : d[2];
                                                        if (header.color_mode == CM_CMYK)
                                                                src_a = d[3] < src_a ? src_a : d[3];
                                                }
                                                else if (layBlend == "smud")
                                                {
                                                        src_r = d[0] + src_r - src_r * d[0] / 128;
                                                        src_g = d[1] + src_g - src_g * d[1] / 128;
                                                        src_b = d[2] + src_b - src_b * d[2] / 128;
                                                        if (header.color_mode == CM_CMYK)
                                                                src_a = d[3] + src_a - src_a * d[3] / 128;
                                                }
                                                else if (layBlend == "div ")
                                                {
                                                        src_r = src_r == 255 ? 255 : ((d[0] * 256) / (255-src_r)) > 255 ? 255 : (d[0] * 256) / (255-src_r);
                                                        src_g = src_g == 255 ? 255 : ((d[1] * 256) / (255-src_g)) > 255 ? 255 : (d[1] * 256) / (255-src_g);
                                                        src_b = src_b == 255 ? 255 : ((d[2] * 256) / (255-src_b)) > 255 ? 255 : (d[2] * 256) / (255-src_b);
                                                        if (header.color_mode == CM_CMYK)
                                                                src_a = src_a == 255 ? 255 : ((d[3] * 256) / (255-src_a)) > 255 ? 255 : (d[3] * 256) / (255-src_a);
                                                }
                                                else if (layBlend == "idiv")
                                                {
                                                        src_r = src_r == 0 ? 0 : (255 - (((255-d[0]) * 256) / src_r)) < 0 ? 0 : 255 - (((255-d[0]) * 256) / src_r);
                                                        src_g = src_g == 0 ? 0 : (255 - (((255-d[1]) * 256) / src_g)) < 0 ? 0 : 255 - (((255-d[1]) * 256) / src_g);
                                                        src_b = src_b == 0 ? 0 : (255 - (((255-d[2]) * 256) / src_b)) < 0 ? 0 : 255 - (((255-d[2]) * 256) / src_b);
                                                        if (header.color_mode == CM_CMYK)
                                                                src_a = src_a == 0 ? 0 : (255 - (((255-d[3]) * 256) / src_a)) < 0 ? 0 : 255 - (((255-d[3]) * 256) / src_a);
                                                }
                                                else if (layBlend == "hue ")
                                                {
                                                        if (header.color_mode != CM_CMYK)
                                                        {
                                                                uchar new_r = d[0];
                                                                uchar new_g = d[1];
                                                                uchar new_b = d[2];
                                                                RGBTOHSV(src_r, src_g, src_b);
                                                                RGBTOHSV(new_r, new_g, new_b);
                                                                new_r = src_r;
                                                                HSVTORGB(new_r, new_g, new_b);
                                                                src_r = new_r;
                                                                src_g = new_g;
                                                                src_b = new_b;
                                                        }
                                                }
                                                else if (layBlend == "sat ")
                                                {
                                                        if (header.color_mode != CM_CMYK)
                                                        {
                                                                uchar new_r = d[0];
                                                                uchar new_g = d[1];
                                                                uchar new_b = d[2];
                                                                RGBTOHSV(src_r, src_g, src_b);
                                                                RGBTOHSV(new_r, new_g, new_b);
                                                                new_g = src_g;
                                                                HSVTORGB(new_r, new_g, new_b);
                                                                src_r = new_r;
                                                                src_g = new_g;
                                                                src_b = new_b;
                                                        }
                                                }
                                                else if (layBlend == "lum ")
                                                {
                                                        if (header.color_mode != CM_CMYK)
                                                        {
                                                                uchar new_r = d[0];
                                                                uchar new_g = d[1];
                                                                uchar new_b = d[2];
                                                                RGBTOHSV(src_r, src_g, src_b);
                                                                RGBTOHSV(new_r, new_g, new_b);
                                                                new_b = src_b;
                                                                HSVTORGB(new_r, new_g, new_b);
                                                                src_r = new_r;
                                                                src_g = new_g;
                                                                src_b = new_b;
                                                        }
                                                }
                                                else if (layBlend == "colr")
                                                {
                                                        if (header.color_mode != CM_CMYK)
                                                        {
                                                                uchar new_r = d[0];
                                                                uchar new_g = d[1];
                                                                uchar new_b = d[2];
                                                                RGBTOHLS(src_r, src_g, src_b);
                                                                RGBTOHLS(new_r, new_g, new_b);
                                                                new_r = src_r;
                                                                new_b = src_b;
                                                                HLSTORGB(new_r, new_g, new_b);
                                                                src_r = new_r;
                                                                src_g = new_g;
                                                                src_b = new_b;
                                                        }
                                                }
                                        }
                                        if (dst_alpha == 0)
                                        {
                                                r = src_r;
                                                g = src_g;
                                                b = src_b;
                                                a = src_a;
                                        }
                                        else
                                        {
                                                if (src_alpha > 0)
                                                {
                                                        r = (d[0] * (255 - src_alpha) + src_r * src_alpha) / 255;
                                                        g = (d[1] * (255 - src_alpha) + src_g * src_alpha) / 255;
                                                        b = (d[2] * (255 - src_alpha) + src_b * src_alpha) / 255;
                                                        if (header.color_mode == CM_CMYK)
                                                                a = (d[3] * (255 - src_alpha) + src_a * src_alpha) / 255;
                                                        if (layBlend != "diss")
                                                                src_alpha = dst_alpha + INT_MULT(255 - dst_alpha, src_alpha);
                                                }
                                        }
                                        if (src_alpha > 0)
                                        {
                                                d[0] = r;
                                                d[1] = g;
                                                d[2] = b;
                                                if (header.color_mode == CM_CMYK)
                                                {
                                                        d[3] = a;
                                                        d[4] = src_alpha;
                                                }
                                                else
                                                        d[3] = src_alpha;
                                        }
                                        s += r2_image.channels();
                                        d += r_image.channels();
                                        if (hasMask)
                                                sm += mask.channels();
                                }
                        }
                }
        }
        *firstLayer = false;
        return true;
}