Example #1
0
KisMetaData::Value cfaPatternExifToKMD(const Exiv2::Value::AutoPtr value, Exiv2::ByteOrder order)
{
    QMap<QString, KisMetaData::Value> cfaPatternStructure;
    const Exiv2::DataValue* dvalue = dynamic_cast<const Exiv2::DataValue*>(&*value);
    Q_ASSERT(dvalue);
    QByteArray array(dvalue->count(), 0);
    dvalue->copy((Exiv2::byte*)array.data());
    int columns = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[0], order);
    int rows = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[1], order);
    if ((columns * rows + 4) != dvalue->count()) { // Sometime byteOrder get messed up (especially if metadata got saved with kexiv2 library, or any library that doesn't save back with the same byte order as the camera)
        order = invertByteOrder(order);
        columns = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[0], order);
        rows = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[1], order);
        Q_ASSERT((columns * rows + 4) == dvalue->count());
    }
    cfaPatternStructure["Columns"] = KisMetaData::Value(columns);
    cfaPatternStructure["Rows"] = KisMetaData::Value(rows);
    QList<KisMetaData::Value> values;
    int index = 4;
    for (int i = 0; i < columns * rows; i++) {
        values.append(KisMetaData::Value(*(array.data() + index)));
        index++;
    }
    cfaPatternStructure["Values"] = KisMetaData::Value(values, KisMetaData::Value::OrderedArray);
    dbgFile << "CFAPattern " << ppVar(columns) << " " << ppVar(rows) << ppVar(values.size() << ppVar(dvalue->count()));
    return KisMetaData::Value(cfaPatternStructure);
}
void testMergeCrossColorSpaceImpl(bool useProjectionColorSpace, bool swapSpaces)
{
    QRect refRect;
    TestUtil::MaskParent p;

    KisPaintLayerSP layer1;
    KisPaintLayerSP layer2;
    KisPaintLayerSP layer3;

    const KoColorSpace *cs2 = useProjectionColorSpace ?
        p.image->colorSpace() : KoColorSpaceRegistry::instance()->lab16();

    const KoColorSpace *cs3 = KoColorSpaceRegistry::instance()->rgb16();

    if (swapSpaces) {
        qSwap(cs2, cs3);
    }

    dbgKrita << "Testing testMergeCrossColorSpaceImpl:";
    dbgKrita << "    " << ppVar(cs2);
    dbgKrita << "    " << ppVar(cs3);

    layer1 = p.layer;
    layer2 = new KisPaintLayer(p.image, "paint2", OPACITY_OPAQUE_U8, cs2);
    layer3 = new KisPaintLayer(p.image, "paint3", OPACITY_OPAQUE_U8, cs3);

    QRect rect1(100, 100, 100, 100);
    QRect rect2(150, 150, 150, 150);
    QRect rect3(250, 250, 200, 200);

    layer1->paintDevice()->fill(rect1, KoColor(Qt::red, layer1->colorSpace()));
    layer2->paintDevice()->fill(rect2, KoColor(Qt::green, layer2->colorSpace()));
    layer3->paintDevice()->fill(rect3, KoColor(Qt::blue, layer3->colorSpace()));

    p.image->addNode(layer2);
    p.image->addNode(layer3);

    p.image->initialRefreshGraph();


    {
        KisLayerSP newLayer = p.image->mergeDown(layer3, KisMetaData::MergeStrategyRegistry::instance()->get("Drop"));
        p.image->waitForDone();

        QCOMPARE(newLayer->colorSpace(), p.image->colorSpace());

        p.undoStore->undo();
    }

    {
        layer2->disableAlphaChannel(true);

        KisLayerSP newLayer = p.image->mergeDown(layer3, KisMetaData::MergeStrategyRegistry::instance()->get("Drop"));
        p.image->waitForDone();

        QCOMPARE(newLayer->colorSpace(), p.image->colorSpace());

        p.undoStore->undo();
    }
}
Example #3
0
inline std::string exiv2Prefix(const KisMetaData::Schema* _schema)
{
    const QByteArray latin1SchemaUri = _schema->uri().toLatin1();
    std::string prefix = Exiv2::XmpProperties::prefix(latin1SchemaUri.constData());
    if (prefix.empty()) {
        dbgFile << "Unknown namespace " << ppVar(_schema->uri()) << ppVar(_schema->prefix());
        prefix = _schema->prefix().toLatin1().constData();
        Exiv2::XmpProperties::registerNs(latin1SchemaUri.constData(), prefix);
    }
    return prefix;
}
QTabletEvent::TabletDevice parseWacomDeviceId(quint32 deviceId)
{
    enum {
        CSR_TYPE_SPECIFIC_MASK = 0x0F06,
        CSR_TYPE_SPECIFIC_STYLUS_BITS = 0x0802,
        CSR_TYPE_SPECIFIC_AIRBRUSH_BITS = 0x0902,
        CSR_TYPE_SPECIFIC_4DMOUSE_BITS = 0x0004,
        CSR_TYPE_SPECIFIC_LENSCURSOR_BITS = 0x0006,
        CSR_TYPE_SPECIFIC_ROTATIONSTYLUS_BITS = 0x0804
    };

    QTabletEvent::TabletDevice device;

    if ((((deviceId & 0x0006) == 0x0002) &&
         ((deviceId & CSR_TYPE_SPECIFIC_MASK) != CSR_TYPE_SPECIFIC_AIRBRUSH_BITS)) || // Bamboo
        deviceId == 0x4020) { // Surface Pro 2 tablet device

        device = QTabletEvent::Stylus;
    } else {
        switch (deviceId & CSR_TYPE_SPECIFIC_MASK) {
        case CSR_TYPE_SPECIFIC_STYLUS_BITS:
            device = QTabletEvent::Stylus;
            break;
        case CSR_TYPE_SPECIFIC_AIRBRUSH_BITS:
            device = QTabletEvent::Airbrush;
            break;
        case CSR_TYPE_SPECIFIC_4DMOUSE_BITS:
            device = QTabletEvent::FourDMouse;
            break;
        case CSR_TYPE_SPECIFIC_LENSCURSOR_BITS:
            device = QTabletEvent::Puck;
            break;
        case CSR_TYPE_SPECIFIC_ROTATIONSTYLUS_BITS:
            device = QTabletEvent::RotationStylus;
            break;
        default:
            if (deviceId != 0) {
                warnKrita << "Unrecognized stylus device found! Falling back to usual \'Stylus\' definition";
                warnKrita << ppVar(deviceId);
                warnKrita << ppVar((deviceId & CSR_TYPE_SPECIFIC_MASK));
            }

            device = QTabletEvent::Stylus;
        }
    }

    return device;
}
void KisGradientPainterTest::testFindShapedExtremums()
{
    QPolygonF selectionPolygon;
    selectionPolygon << QPointF(100, 100);
    selectionPolygon << QPointF(200, 120);
    selectionPolygon << QPointF(170, 140);
    selectionPolygon << QPointF(200, 180);
    selectionPolygon << QPointF(30, 220);

    QPolygonF selectionErasePolygon;
    selectionErasePolygon << QPointF(101, 101);
    selectionErasePolygon << QPointF(190, 120);
    selectionErasePolygon << QPointF(160, 140);
    selectionErasePolygon << QPointF(200, 180);
    selectionErasePolygon << QPointF(30, 220);

    QPainterPath path;
    path.addPolygon(selectionPolygon);
    path.closeSubpath();
    path.addPolygon(selectionErasePolygon);
    path.closeSubpath();

    QPointF center =
        KisPolygonalGradientShapeStrategy::testingCalculatePathCenter(
            4, path, 2.0, true);

    dbgKrita << ppVar(center);

    QVERIFY(path.contains(center));
}
Example #6
0
Exiv2::Value* cfaPatternKMDToExif(const KisMetaData::Value& value)
{
    QMap<QString, KisMetaData::Value> cfaStructure = value.asStructure();
    quint16 columns = cfaStructure["Columns"].asVariant().toInt(0);
    quint16 rows = cfaStructure["Rows"].asVariant().toInt(0);

    QList<KisMetaData::Value> values = cfaStructure["Values"].asArray();
    Q_ASSERT(columns*rows == values.size());
    QByteArray array(4 + columns*rows, 0);
    (reinterpret_cast<quint16*>(array.data()))[0] = columns;
    (reinterpret_cast<quint16*>(array.data()))[1] = rows;
    for (int i = 0; i < columns * rows; i++) {
        int val = values[i].asVariant().toInt();
        *(array.data() + 4 + i) = val;
    }
    dbgFile << "Cfa Array " << ppVar(columns) << ppVar(rows) << ppVar(array.size());
    return new Exiv2::DataValue((const Exiv2::byte*)array.data(), array.size());
}
void writeFixedString(const QString &value, QIODevice *device)
{
    KIS_ASSERT_RECOVER_RETURN(value.length() == 4);

    if (!device->write(value.toAscii().data(), value.length())) {
        warnKrita << "WARNING: ASL: Failed to write ASL string" << ppVar(value);
        return;
    }
}
Example #8
0
KisMetaData::Value exifOECFToKMDOECFStructure(const Exiv2::Value::AutoPtr value, Exiv2::ByteOrder order)
{
    QMap<QString, KisMetaData::Value> oecfStructure;
    const Exiv2::DataValue* dvalue = dynamic_cast<const Exiv2::DataValue*>(&*value);
    Q_ASSERT(dvalue);
    QByteArray array(dvalue->count(), 0);
    dvalue->copy((Exiv2::byte*)array.data());
    int columns = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[0], order);
    int rows = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[1], order);

    if ((columns * rows + 4) > dvalue->count()) { // Sometime byteOrder get messed up (especially if metadata got saved with kexiv2 library, or any library that doesn't save back with the same byte order as the camera)
        order = invertByteOrder(order);
        columns = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[0], order);
        rows = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[1], order);
        Q_ASSERT((columns * rows + 4) > dvalue->count());
    }
    oecfStructure["Columns"] = KisMetaData::Value(columns);
    oecfStructure["Rows"] = KisMetaData::Value(rows);
    int index = 4;
    QList<KisMetaData::Value> names;
    for (int i = 0; i < columns; i++) {
        int lastIndex = array.indexOf((char)0, index);
        QString name = array.mid(index, lastIndex - index);
        if (index != lastIndex) {
            index = lastIndex + 1;
            dbgFile << "Name [" << i << "] =" << name;
            names.append(KisMetaData::Value(name));
        } else {
            names.append(KisMetaData::Value(""));
        }
    }
    oecfStructure["Names"] = KisMetaData::Value(names, KisMetaData::Value::OrderedArray);
    QList<KisMetaData::Value> values;
    qint16* dataIt = reinterpret_cast<qint16*>(array.data() + index);
    for (int i = 0; i < columns; i++) {
        for (int j = 0; j < rows; j++) {
            values.append(KisMetaData::Value(KisMetaData::Rational(fixEndianess<qint32>(dataIt[0], order), fixEndianess<qint32>(dataIt[1], order))));
            dataIt += 8;
        }
    }
    oecfStructure["Values"] = KisMetaData::Value(values, KisMetaData::Value::OrderedArray);
    dbgFile << "OECF: " << ppVar(columns) << ppVar(rows) << ppVar(dvalue->count());
    return KisMetaData::Value(oecfStructure);
}
    // Write UUID fetched from the file name or generate
QString getPatternUuidLazy(const KoPattern *pattern)
{
    QUuid uuid;
    QString patternFileName = pattern->filename();

    if (patternFileName.endsWith(".pat", Qt::CaseInsensitive)) {
        QString strUuid = patternFileName.left(patternFileName.size() - 4);

        uuid = QUuid(strUuid);
    }

    if (uuid.isNull()) {
        warnKrita << "WARNING: Saved pattern doesn't have a UUID, generating...";
        warnKrita << ppVar(patternFileName) << ppVar(pattern->name());
        uuid = QUuid::createUuid();
    }

    return uuid.toString().mid(1, 36);
}
void writePascalString(const QString &value, QIODevice *device)
{
    quint8 lenTag = value.length();
    SAFE_WRITE_EX(device, lenTag);

    if (!device->write(value.toAscii().data(), value.length())) {
        warnKrita << "WARNING: ASL: Failed to write ASL string" << ppVar(value);
        return;
    }
}
void KisAslLayerStyleSerializerTest::testWritingGlobalPatterns()
{
    KisPSDLayerStyleSP style(new KisPSDLayerStyle());

    KoResourceServer<KoPattern> *server = KoResourceServerProvider::instance()->patternServer();
    QList<KoPattern*> sortedResources = server->sortedResources();

    KoPattern *pattern = sortedResources.first();
    Q_ASSERT(pattern);

    dbgKrita << ppVar(pattern->name());
    dbgKrita << ppVar(pattern->filename());

    style->patternOverlay()->setEffectEnabled(true);
    style->patternOverlay()->setPattern(pattern);

    {
        KisAslLayerStyleSerializer s;

        s.setStyles(QVector<KisPSDLayerStyleSP>() << style);

        QFile dstFile("test_written_pattern_only.asl");
        dstFile.open(QIODevice::WriteOnly);
        s.saveToDevice(&dstFile);
        dstFile.close();
    }
/*
    QByteArray resultXMLDoc;

    {
        QFile resultFile("test_written.asl");
        resultFile.open(QIODevice::ReadOnly);

        KisAslReader reader;
        QDomDocument doc = reader.readFile(&resultFile);
        resultXMLDoc = doc.toByteArray();
    }
*/

}
bool KisZoomAndPanTest::checkPan(ZoomAndPanTester &t, QPoint shift)
{
    QPoint oldOffset = t.coordinatesConverter()->documentOffset();
    QPointF oldPrefCenter = t.canvasController()->preferredCenter();

    t.canvasController()->pan(shift);

    QPoint newOffset  = t.coordinatesConverter()->documentOffset();
    QPointF newPrefCenter = t.canvasController()->preferredCenter();
    QPointF newTopLeft = t.coordinatesConverter()->imageRectInWidgetPixels().topLeft();

    QPoint expectedOffset  = oldOffset + shift;
    QPointF expectedPrefCenter = oldPrefCenter + shift;


    // no tolerance accepted for pan
    bool offsetAsExpected = newOffset == expectedOffset;

    // rounding can happen due to the scroll bars being the main
    // source of the offset
    bool preferredCenterAsExpected =
        compareWithRounding(expectedPrefCenter, newPrefCenter, 1.0);

    bool topLeftAsExpected = newTopLeft.toPoint() == -newOffset;

    if (!offsetAsExpected ||
        !preferredCenterAsExpected ||
        !topLeftAsExpected) {

        qDebug() << "***** PAN *****************";

        if(!offsetAsExpected) {
            qDebug() << " ### Offset invariant broken";
        }

        if(!preferredCenterAsExpected) {
            qDebug() << " ### Preferred center invariant broken";
        }

        if(!topLeftAsExpected) {
            qDebug() << " ### TopLeft invariant broken";
        }

        qDebug() << ppVar(expectedOffset);
        qDebug() << ppVar(expectedPrefCenter);
        qDebug() << ppVar(oldOffset) << ppVar(newOffset);
        qDebug() << ppVar(oldPrefCenter) << ppVar(newPrefCenter);
        qDebug() << ppVar(newTopLeft);
        qDebug() << "***************************";
    }

    return offsetAsExpected && preferredCenterAsExpected && topLeftAsExpected;
}
void KisTileCompressorsTest::doLowLevelRoundTripIncompressible(KisAbstractTileCompressor *compressor)
{
    const qint32 pixelSize = 1;
    quint8 oddPixel1 = 128;
    quint8 oddPixel2 = 129;

    QFile file(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return;

    QByteArray incompressibleArray = file.readAll();

    /**
     * A small hack to acquire a standalone tile data.
     * globalTileDataStore is not exported out of kritaimage.so,
     * so we get it from the data manager
     */
    KisTiledDataManager dm(pixelSize, &oddPixel1);
    KisTileSP tile = dm.getTile(0, 0, true);
    tile->lockForWrite();


    KisTileData *td = tile->tileData();
    QVERIFY(memoryIsFilled(oddPixel1, td->data(), TILESIZE));

    memcpy(td->data(), incompressibleArray.data(), TILESIZE);
    QVERIFY(!memcmp(td->data(), incompressibleArray.data(), TILESIZE));

    qint32 bufferSize = compressor->tileDataBufferSize(td);
    quint8 *buffer = new quint8[bufferSize];
    qint32 bytesWritten;
    compressor->compressTileData(td, buffer, bufferSize, bytesWritten);
    dbgKrita << ppVar(bytesWritten);


    memset(td->data(), oddPixel2, TILESIZE);
    QVERIFY(memoryIsFilled(oddPixel2, td->data(), TILESIZE));

    compressor->decompressTileData(buffer, bytesWritten, td);

    QVERIFY(!memcmp(td->data(), incompressibleArray.data(), TILESIZE));

    delete[] buffer;
    tile->unlock();
}
void KisAslLayerStyleSerializerTest::testReadMultipleStyles()
{
    KisPSDLayerStyleSP style(new KisPSDLayerStyle());

    QVector<KisPSDLayerStyleSP> styles;

    {
        KisAslLayerStyleSerializer s;

        QString srcFileName(TestUtil::fetchDataFileLazy("asl/multiple_styles.asl"));
        QFile aslFile(srcFileName);
        aslFile.open(QIODevice::ReadOnly);
        s.readFromDevice(&aslFile);

        styles = s.styles();
    }


    {
        KisAslLayerStyleSerializer s;

        QString dstFileName("multiple_styles_out.asl");
        QFile aslFile(dstFileName);
        aslFile.open(QIODevice::WriteOnly);

        s.setStyles(styles);
        s.saveToDevice(&aslFile);
    }

    {
        KisAslLayerStyleSerializer s;

        QString srcFileName("multiple_styles_out.asl");
        QFile aslFile(srcFileName);
        aslFile.open(QIODevice::ReadOnly);
        s.readFromDevice(&aslFile);

        styles = s.styles();

        dbgKrita << ppVar(styles.size());
    }
}
void KisTileCompressorsTest::doLowLevelRoundTrip(KisAbstractTileCompressor *compressor)
{
    const qint32 pixelSize = 1;
    quint8 oddPixel1 = 128;
    quint8 oddPixel2 = 129;

    /**
     * A small hack to acquire a standalone tile data.
     * globalTileDataStore is not exported out of kritaimage.so,
     * so we get it from the data manager
     */
    KisTiledDataManager dm(pixelSize, &oddPixel1);
    KisTileSP tile = dm.getTile(0, 0, true);
    tile->lockForWrite();


    KisTileData *td = tile->tileData();
    QVERIFY(memoryIsFilled(oddPixel1, td->data(), TILESIZE));

    qint32 bufferSize = compressor->tileDataBufferSize(td);
    quint8 *buffer = new quint8[bufferSize];
    qint32 bytesWritten;
    compressor->compressTileData(td, buffer, bufferSize, bytesWritten);
    dbgKrita << ppVar(bytesWritten);


    memset(td->data(), oddPixel2, TILESIZE);
    QVERIFY(memoryIsFilled(oddPixel2, td->data(), TILESIZE));

    compressor->decompressTileData(buffer, bytesWritten, td);

    QVERIFY(memoryIsFilled(oddPixel1, td->data(), TILESIZE));

    delete[] buffer;
    tile->unlock();
}
void KisSafeDocumentLoader::delayedLoadStart()
{
    QFileInfo originalInfo(m_d->path);
    QFileInfo tempInfo(m_d->temporaryPath);
    bool successfullyLoaded = false;

    if (!m_d->fileChangedFlag &&
        originalInfo.size() == m_d->initialFileSize &&
        originalInfo.lastModified() == m_d->initialFileTimeStamp &&
        tempInfo.size() == m_d->initialFileSize) {

        m_d->doc.reset(KisPart::instance()->createDocument());
        successfullyLoaded = m_d->doc->openUrl(QUrl::fromLocalFile(m_d->temporaryPath),
                                               KisDocument::OPEN_URL_FLAG_DO_NOT_ADD_TO_RECENT_FILES);
    } else {
        dbgKrita << "File was modified externally. Restarting.";
        dbgKrita << ppVar(m_d->fileChangedFlag);
        dbgKrita << ppVar(m_d->initialFileSize);
        dbgKrita << ppVar(m_d->initialFileTimeStamp);
        dbgKrita << ppVar(originalInfo.size());
        dbgKrita << ppVar(originalInfo.lastModified());
        dbgKrita << ppVar(tempInfo.size());
    }

    QFile::remove(m_d->temporaryPath);
    m_d->isLoading = false;

    if (!successfullyLoaded) {
        // Restart the attempt
        m_d->fileChangedSignalCompressor.start();
    } else {
        emit loadingFinished(m_d->doc->image());
    }

    m_d->doc.reset();
}
Example #17
0
bool KisXMPIO::saveTo(KisMetaData::Store* store, QIODevice* ioDevice, HeaderType headerType) const
{
    dbgFile << "Save XMP Data";
    Exiv2::XmpData xmpData_;

    for (QHash<QString, KisMetaData::Entry>::const_iterator it = store->begin();
            it != store->end(); ++it) {
        const KisMetaData::Entry& entry = *it;

        // Check whether the prefix and namespace are know to exiv2
        std::string prefix = exiv2Prefix(entry.schema());
        dbgFile << "Saving " << entry.name();

        const KisMetaData::Value& value = entry.value();

        const KisMetaData::TypeInfo* typeInfo = entry.schema()->propertyType(entry.name());
        if (value.type() == KisMetaData::Value::Structure) {
            QMap<QString, KisMetaData::Value> structure = value.asStructure();
            const KisMetaData::Schema* structureSchema = 0;
            if (typeInfo) {
                structureSchema = typeInfo->structureSchema();
            }
            if (!structureSchema) {
                dbgFile << "Unknown schema for " << entry.name();
                structureSchema = entry.schema();
            }
            Q_ASSERT(structureSchema);
            saveStructure(xmpData_, entry.name(), prefix, structure, structureSchema);
        } else {
            Exiv2::XmpKey key(prefix, entry.name().toLatin1().constData());
            if (typeInfo && (typeInfo->propertyType() == KisMetaData::TypeInfo::OrderedArrayType
                             || typeInfo->propertyType() == KisMetaData::TypeInfo::UnorderedArrayType
                             || typeInfo->propertyType() == KisMetaData::TypeInfo::AlternativeArrayType)
                    && typeInfo->embeddedPropertyType()->propertyType() == KisMetaData::TypeInfo::StructureType) {
                // Here is the bad part, again we need to do it by hand
                Exiv2::XmpTextValue tv;
                switch (typeInfo->propertyType()) {
                case KisMetaData::TypeInfo::OrderedArrayType:
                    tv.setXmpArrayType(Exiv2::XmpValue::xaSeq);
                    break;
                case KisMetaData::TypeInfo::UnorderedArrayType:
                    tv.setXmpArrayType(Exiv2::XmpValue::xaBag);
                    break;
                case KisMetaData::TypeInfo::AlternativeArrayType:
                    tv.setXmpArrayType(Exiv2::XmpValue::xaAlt);
                    break;
                default:
                    // Cannot happen
                    ;
                }
                xmpData_.add(key, &tv); // set the arrya type
                const KisMetaData::TypeInfo* stuctureTypeInfo = typeInfo->embeddedPropertyType();
                const KisMetaData::Schema* structureSchema = 0;
                if (stuctureTypeInfo) {
                    structureSchema = stuctureTypeInfo->structureSchema();
                }
                if (!structureSchema) {
                    dbgFile << "Unknown schema for " << entry.name();
                    structureSchema = entry.schema();
                }
                Q_ASSERT(structureSchema);
                QList<KisMetaData::Value> array = value.asArray();
                for (int idx = 0; idx < array.size(); ++idx) {
                    saveStructure(xmpData_, QString("%1[%2]").arg(entry.name()).arg(idx + 1), prefix, array[idx].asStructure(), structureSchema);
                }
            } else {
                dbgFile << ppVar(key.key().c_str());
                Exiv2::Value *v = kmdValueToExivXmpValue(value);
                if (v) {
                    xmpData_.add(key, v);
                }
            }
        }
        // TODO property qualifier
    }
    // Serialize data
    std::string xmpPacket_;
    Exiv2::XmpParser::encode(xmpPacket_, xmpData_);
    // Save data into the IO device
    ioDevice->open(QIODevice::WriteOnly);
    if (headerType == KisMetaData::IOBackend::JpegHeader) {
        xmpPacket_ = "http://ns.adobe.com/xap/1.0/\0" + xmpPacket_;
    }
    ioDevice->write(xmpPacket_.c_str(), xmpPacket_.length());
    return true;
}
Example #18
0
bool KisExifIO::loadFrom(KisMetaData::Store* store, QIODevice* ioDevice) const
{
    ioDevice->open(QIODevice::ReadOnly);
    if (!ioDevice->isOpen()) {
        return false;
    }
    QByteArray arr = ioDevice->readAll();
    Exiv2::ExifData exifData;
    Exiv2::ByteOrder byteOrder;
#if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 17
    exifData.load((const Exiv2::byte*)arr.data(), arr.size());
    byteOrder = exifData.byteOrder();
#else
    try {
        byteOrder = Exiv2::ExifParser::decode(exifData, (const Exiv2::byte*)arr.data(), arr.size());
    }
    catch (const std::exception& ex) {
        warnKrita << "Received exception trying to parse exiv data" << ex.what();
        return false;
    }
    catch (...) {
        dbgKrita << "Received unknown exception trying to parse exiv data";
        return false;
    }

#endif
    dbgFile << "Byte order = " << byteOrder << ppVar(Exiv2::bigEndian) << ppVar(Exiv2::littleEndian);
    dbgFile << "There are" << exifData.count() << " entries in the exif section";
    const KisMetaData::Schema* tiffSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::TIFFSchemaUri);
    Q_ASSERT(tiffSchema);
    const KisMetaData::Schema* exifSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::EXIFSchemaUri);
    Q_ASSERT(exifSchema);
    const KisMetaData::Schema* dcSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::DublinCoreSchemaUri);
    Q_ASSERT(dcSchema);
    const KisMetaData::Schema* xmpSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::XMPSchemaUri);
    Q_ASSERT(xmpSchema);
    for (Exiv2::ExifMetadata::const_iterator it = exifData.begin();
            it != exifData.end(); ++it) {
        if (it->key() == "Exif.Photo.StripOffsets"
                || it->key() == "RowsPerStrip"
                || it->key() == "StripByteCounts"
                || it->key() == "JPEGInterchangeFormat"
                || it->key() == "JPEGInterchangeFormatLength"
                || it->tagName() == "0x0000" ) {
            dbgFile << it->key().c_str() << " is ignored";
        } else if (it->key() == "Exif.Photo.MakerNote") {
            const KisMetaData::Schema* makerNoteSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::MakerNoteSchemaUri);
            store->addEntry(KisMetaData::Entry(makerNoteSchema, "RawData", exivValueToKMDValue(it->getValue(), false)));
        } else if (it->key() == "Exif.Image.DateTime") { // load as xmp:ModifyDate
            store->addEntry(KisMetaData::Entry(xmpSchema, "ModifyDate", KisMetaData::Value(exivValueToDateTime(it->getValue()))));
        } else if (it->key() == "Exif.Image.ImageDescription") { // load as "dc:description"
            store->addEntry(KisMetaData::Entry(dcSchema, "description", exivValueToKMDValue(it->getValue(), false)));
        } else if (it->key() == "Exif.Image.Software") { // load as "xmp:CreatorTool"
            store->addEntry(KisMetaData::Entry(xmpSchema, "CreatorTool", exivValueToKMDValue(it->getValue(), false)));
        } else if (it->key() == "Exif.Image.Artist") { // load as dc:creator
            QList<KisMetaData::Value> creators;
            creators.push_back(exivValueToKMDValue(it->getValue(), false));
            store->addEntry(KisMetaData::Entry(dcSchema, "creator", KisMetaData::Value(creators, KisMetaData::Value::OrderedArray)));
        } else if (it->key() == "Exif.Image.Copyright") { // load as dc:rights
            store->addEntry(KisMetaData::Entry(dcSchema, "rights", exivValueToKMDValue(it->getValue(), false)));
        } else if (it->groupName() == "Image") {
            // Tiff tags
            QString fixedTN = it->tagName().c_str();
            if (it->key() == "Exif.Image.ExifTag") {
                dbgFile << "Ignoring " << it->key().c_str();
            } else if (KisMetaData::Entry::isValidName(fixedTN)) {
                store->addEntry(KisMetaData::Entry(tiffSchema, fixedTN, exivValueToKMDValue(it->getValue(), false))) ;
            } else {
                dbgFile << "Invalid tag name: " << fixedTN;
            }
        } else if (it->groupName() == "Photo" || (it->groupName() == "GPS")) {
            // Exif tags (and GPS tags)
            KisMetaData::Value v;
            if (it->key() == "Exif.Photo.ExifVersion" || it->key() == "Exif.Photo.FlashpixVersion") {
                v = exifVersionToKMDValue(it->getValue());
            } else if (it->key() == "Exif.Photo.FileSource") {
                v = KisMetaData::Value(3);
            } else if (it->key() == "Exif.Photo.SceneType") {
                v = KisMetaData::Value(1);
            } else if (it->key() == "Exif.Photo.ComponentsConfiguration") {
                v = exifArrayToKMDIntOrderedArray(it->getValue());
            } else if (it->key() == "Exif.Photo.OECF") {
                v = exifOECFToKMDOECFStructure(it->getValue(), byteOrder);
            } else if (it->key() == "Exif.Photo.DateTimeDigitized" || it->key() == "Exif.Photo.DateTimeOriginal") {
                v = KisMetaData::Value(exivValueToDateTime(it->getValue()));
            } else if (it->key() == "Exif.Photo.DeviceSettingDescription") {
                v = deviceSettingDescriptionExifToKMD(it->getValue());
            } else if (it->key() == "Exif.Photo.CFAPattern") {
                v = cfaPatternExifToKMD(it->getValue(), byteOrder);
            } else if (it->key() == "Exif.Photo.Flash") {
                v = flashExifToKMD(it->getValue());
            } else if (it->key() == "Exif.Photo.UserComment") {
                KisMetaData::Value vUC = exivValueToKMDValue(it->getValue(), false);
                Q_ASSERT(vUC.type() == KisMetaData::Value::Variant);
                QVariant commentVar = vUC.asVariant();
                QString comment;
                if (commentVar.type() == QVariant::String) {
                    comment = commentVar.toString();
                } else if (commentVar.type() == QVariant::ByteArray) {
                    const QByteArray commentString = commentVar.toByteArray();
                    comment = QString::fromLatin1(commentString.constData(), commentString.size());
                } else {
                    warnKrita << "KisExifIO: Unhandled UserComment value type.";
                }
                KisMetaData::Value vcomment(comment);
                vcomment.addPropertyQualifier("xml:lang", KisMetaData::Value("x-default"));
                QList<KisMetaData::Value> alt;
                alt.append(vcomment);
                v = KisMetaData::Value(alt, KisMetaData::Value::LangArray);
            } else {
                bool forceSeq = false;
                KisMetaData::Value::ValueType arrayType = KisMetaData::Value::UnorderedArray;
                if (it->key() == "Exif.Photo.ISOSpeedRatings") {
                    forceSeq = true;
                    arrayType = KisMetaData::Value::OrderedArray;
                }
                v = exivValueToKMDValue(it->getValue(), forceSeq, arrayType);
            }
            if (it->key() == "Exif.Photo.InteroperabilityTag" || it->key() == "Exif.Photo.0xea1d") { // InteroperabilityTag isn't useful for XMP, 0xea1d isn't a valid Exif tag
                dbgFile << "Ignoring " << it->key().c_str();
            } else {
                store->addEntry(KisMetaData::Entry(exifSchema, it->tagName().c_str(), v));
            }
        } else if (it->groupName() == "Thumbnail") {
            dbgFile << "Ignoring thumbnail tag :" << it->key().c_str();
        } else {
            dbgFile << "Unknown exif tag, cannot load:" << it->key().c_str();
        }
    }
    ioDevice->close();
    return true;
}
void KisZoomAndPanTest::testRotation(qreal vastScrolling, qreal zoom)
{
    KisConfig cfg;
    cfg.setVastScrolling(vastScrolling);

    ZoomAndPanTester t;

    QCOMPARE(t.image()->size(), QSize(640,441));
    QCOMPARE(t.image()->xRes(), 1.0);
    QCOMPARE(t.image()->yRes(), 1.0);

    QPointF preferredCenter = zoom * t.image()->bounds().center();

    t.canvasController()->resize(QSize(500,500));
    t.zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, zoom);
    t.canvasController()->setPreferredCenter(preferredCenter.toPoint());

    QCOMPARE(t.canvasWidget()->size(), QSize(483,483));
    QCOMPARE(t.canvasWidget()->size(), t.canvasController()->viewportSize());

    QPointF realCenterPoint = t.coordinatesConverter()->widgetToImage(t.coordinatesConverter()->widgetCenterPoint());
    QPointF expectedCenterPoint = QPointF(t.image()->bounds().center());

    if(!compareWithRounding(realCenterPoint, expectedCenterPoint, 2/zoom)) {
        qDebug() << "Failed to set initial center point";
        qDebug() << ppVar(expectedCenterPoint) << ppVar(realCenterPoint);
        QFAIL("FAIL: Failed to set initial center point");
    }

    QVERIFY(checkRotation(t, 30));
    QVERIFY(checkRotation(t, 20));
    QVERIFY(checkRotation(t, 10));
    QVERIFY(checkRotation(t, 5));
    QVERIFY(checkRotation(t, 5));
    QVERIFY(checkRotation(t, 5));

    if(vastScrolling < 0.5 && zoom < 1) {
        qWarning() << "Disabling a few tests for vast scrolling ="
                   << vastScrolling << ". See comment for more";
        /**
         * We have to disable a couple of tests here for the case when
         * vastScrolling value is 0.2. The problem is that the centering
         * correction applied  to the offset in
         * KisCanvasController::rotateCanvas pollutes the preferredCenter
         * value, because KoCnvasControllerWidget has no access to this
         * correction and cannot calculate the real value of the center of
         * the image. To fix this bug the calculation of correction
         * (aka "origin") should be moved to the KoCanvasControllerWidget
         * itself which would cause quite huge changes (including the change
         * of the external interface of it). Namely, we would have to
         * *calculate* offset from the value of the scroll bars, but not
         * use their values directly:
         *
         * offset = scrollBarValue - origin
         *
         * So now we just disable these unittests and allow a couple
         * of "jumping" bugs appear in vastScrolling < 0.5 modes, which
         * is, actually, not the default case.
         */

    } else {
        QVERIFY(checkRotation(t, 5));
        QVERIFY(checkRotation(t, 5));
        QVERIFY(checkRotation(t, 5));
    }
}
bool KisZoomAndPanTest::checkRotation(ZoomAndPanTester &t, qreal angle)
{
    // save old values
    QPoint oldOffset = t.coordinatesConverter()->documentOffset();
    QPointF oldCenteringCorrection = t.coordinatesConverter()->centeringCorrection();
    QPointF oldPreferredCenter = t.canvasController()->preferredCenter();
    QPointF oldRealCenterPoint = t.coordinatesConverter()->widgetToImage(t.coordinatesConverter()->widgetCenterPoint());
    QSize oldDocumentSize = t.canvasController()->documentSize();

    qreal baseAngle = t.coordinatesConverter()->rotationAngle();
    t.canvasController()->rotateCanvas(angle);

    // save result values
    QPoint newOffset = t.coordinatesConverter()->documentOffset();
    QPointF newCenteringCorrection = t.coordinatesConverter()->centeringCorrection();
    QPointF newPreferredCenter = t.canvasController()->preferredCenter();
    QPointF newRealCenterPoint = t.coordinatesConverter()->widgetToImage(t.coordinatesConverter()->widgetCenterPoint());
    QSize newDocumentSize = t.canvasController()->documentSize();


    // calculate theoretical preferred center
    QTransform rot;
    rot.rotate(angle);

    QSizeF dSize = t.coordinatesConverter()->imageSizeInFlakePixels();
    QPointF dPoint(dSize.width(), dSize.height());

    QPointF expectedPreferredCenter =
        (oldPreferredCenter - dPoint * correctionMatrix(baseAngle)) * rot +
         dPoint * correctionMatrix(baseAngle + angle);

    // calculate theoretical offset based on the real preferred center
    QPointF wPoint(t.canvasWidget()->size().width(), t.canvasWidget()->size().height());
    QPointF expectedOldOffset = oldPreferredCenter - 0.5 * wPoint;
    QPointF expectedNewOffset = newPreferredCenter - 0.5 * wPoint;

    bool preferredCenterAsExpected =
        compareWithRounding(expectedPreferredCenter, newPreferredCenter, 2);
    bool oldOffsetAsExpected =
        compareWithRounding(expectedOldOffset + oldCenteringCorrection, QPointF(oldOffset), 2);
    bool newOffsetAsExpected =
        compareWithRounding(expectedNewOffset + newCenteringCorrection, QPointF(newOffset), 3);

    qreal zoom = t.zoomController()->zoomAction()->effectiveZoom();
    bool realCenterPointAsExpected =
        compareWithRounding(oldRealCenterPoint, newRealCenterPoint, 2/zoom);


    if (!oldOffsetAsExpected ||
        !newOffsetAsExpected ||
        !preferredCenterAsExpected ||
        !realCenterPointAsExpected) {

        qDebug() << "***** ROTATE **************";

        if(!oldOffsetAsExpected) {
            qDebug() << " ### Old offset invariant broken";
        }

        if(!newOffsetAsExpected) {
            qDebug() << " ### New offset invariant broken";
        }

        if(!preferredCenterAsExpected) {
            qDebug() << " ### Preferred center invariant broken";
        }

        if(!realCenterPointAsExpected) {
            qDebug() << " ### *Real* center invariant broken";
        }

        qDebug() << ppVar(expectedOldOffset);
        qDebug() << ppVar(expectedNewOffset);
        qDebug() << ppVar(expectedPreferredCenter);
        qDebug() << ppVar(oldOffset) << ppVar(newOffset);
        qDebug() << ppVar(oldCenteringCorrection) << ppVar(newCenteringCorrection);
        qDebug() << ppVar(oldPreferredCenter) << ppVar(newPreferredCenter);
        qDebug() << ppVar(oldRealCenterPoint) << ppVar(newRealCenterPoint);
        qDebug() << ppVar(oldDocumentSize) << ppVar(newDocumentSize);
        qDebug() << ppVar(baseAngle) << "deg";
        qDebug() << ppVar(angle) << "deg";
        qDebug() << "***************************";
    }

    return preferredCenterAsExpected && oldOffsetAsExpected && newOffsetAsExpected && realCenterPointAsExpected;
}
bool KisZoomAndPanTest::checkInvariants(const QPointF &baseFlakePoint,
                                        const QPoint &oldOffset,
                                        const QPointF &oldPreferredCenter,
                                        qreal oldZoom,
                                        const QPoint &newOffset,
                                        const QPointF &newPreferredCenter,
                                        qreal newZoom,
                                        const QPointF &newTopLeft,
                                        const QSize &oldDocumentSize)
{
    qreal k = newZoom / oldZoom;

    QPointF expectedOffset = oldOffset + (k - 1) * baseFlakePoint;
    QPointF expectedPreferredCenter = oldPreferredCenter + (k - 1) * baseFlakePoint;

    qreal oldPreferredCenterFractionX = 1.0 * oldPreferredCenter.x() / oldDocumentSize.width();
    qreal oldPreferredCenterFractionY = 1.0 * oldPreferredCenter.y() / oldDocumentSize.height();

    qreal roundingTolerance =
        qMax(1.0, qMax(oldPreferredCenterFractionX, oldPreferredCenterFractionY) / k);

    /**
     * In the computation of the offset two roundings happen:
     * first for the computation of oldOffset and the second
     * for the computation of newOffset. So the maximum tolerance
     * should equal 2.
     */
    bool offsetAsExpected =
        compareWithRounding(expectedOffset, QPointF(newOffset), 2 * roundingTolerance);

    /**
     * Rounding for the preferred center happens due to the rounding
     * of the document size while zooming. The wider the step of the
     * zooming, the bigger tolerance should be
     */
    bool preferredCenterAsExpected =
        compareWithRounding(expectedPreferredCenter, newPreferredCenter,
                            roundingTolerance);

    bool topLeftAsExpected = newTopLeft.toPoint() == -newOffset;

    if (!offsetAsExpected ||
        !preferredCenterAsExpected ||
        !topLeftAsExpected) {

        qDebug() << "***** ZOOM ****************";

        if(!offsetAsExpected) {
            qDebug() << " ### Offset invariant broken";
        }

        if(!preferredCenterAsExpected) {
            qDebug() << " ### Preferred center invariant broken";
        }

        if(!topLeftAsExpected) {
            qDebug() << " ### TopLeft invariant broken";
        }

        qDebug() << ppVar(expectedOffset);
        qDebug() << ppVar(expectedPreferredCenter);
        qDebug() << ppVar(oldOffset) << ppVar(newOffset);
        qDebug() << ppVar(oldPreferredCenter) << ppVar(newPreferredCenter);
        qDebug() << ppVar(oldPreferredCenterFractionX);
        qDebug() << ppVar(oldPreferredCenterFractionY);
        qDebug() << ppVar(oldZoom) << ppVar(newZoom);
        qDebug() << ppVar(baseFlakePoint);
        qDebug() << ppVar(newTopLeft);
        qDebug() << ppVar(roundingTolerance);
        qDebug() << "***************************";
    }

    return offsetAsExpected && preferredCenterAsExpected && topLeftAsExpected;
}
Example #22
0
KisImportExportFilter::ConversionStatus KisXCFImport::loadFromDevice(QIODevice* device, KisDocument* doc)
{
    dbgFile << "Start decoding file";
    // Read the file into memory
    device->open(QIODevice::ReadOnly);
    QByteArray data = device->readAll();
    xcf_file = (uint8_t*)data.data();
    xcf_length = data.size();
    device->close();

    // Decode the data
    getBasicXcfInfo() ;
    initColormap();

    dbgFile << XCF.version << "width = " << XCF.width << "height = " << XCF.height << "layers = " << XCF.numLayers;

    // Create the image
    KisImageSP image = new KisImage(doc->createUndoStore(), XCF.width, XCF.height, KoColorSpaceRegistry::instance()->rgb8(), "built image");

    QVector<Layer> layers;
    uint maxDepth = 0;

    // Read layers
    for (int i = 0; i < XCF.numLayers; ++i) {

        Layer layer;

        xcfLayer& xcflayer = XCF.layers[i];
        dbgFile << i << " name = " << xcflayer.name << " opacity = " << xcflayer.opacity << "group:" << xcflayer.isGroup << xcflayer.pathLength;
        dbgFile << ppVar(xcflayer.dim.width) << ppVar(xcflayer.dim.height) << ppVar(xcflayer.dim.tilesx) << ppVar(xcflayer.dim.tilesy) << ppVar(xcflayer.dim.ntiles) << ppVar(xcflayer.dim.c.t) << ppVar(xcflayer.dim.c.l) << ppVar(xcflayer.dim.c.r) << ppVar(xcflayer.dim.c.b);

        maxDepth = qMax(maxDepth, xcflayer.pathLength);

        bool isRgbA = false;
        // Select the color space
        const KoColorSpace* colorSpace = 0;
        switch (xcflayer.type) {
        case GIMP_INDEXED_IMAGE:
        case GIMP_INDEXEDA_IMAGE:
        case GIMP_RGB_IMAGE:
        case GIMP_RGBA_IMAGE:
            colorSpace = KoColorSpaceRegistry::instance()->rgb8();
            isRgbA = true;
            break;
        case GIMP_GRAY_IMAGE:
        case GIMP_GRAYA_IMAGE:
            colorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), "");
            isRgbA = false;
            break;
        }

        // Create the layer
        KisLayerSP kisLayer;
        if (xcflayer.isGroup) {
            kisLayer = new KisGroupLayer(image, QString::fromUtf8(xcflayer.name), xcflayer.opacity);
        }
        else {
            kisLayer = new KisPaintLayer(image, QString::fromUtf8(xcflayer.name), xcflayer.opacity, colorSpace);
        }

        // Set some properties
        kisLayer->setCompositeOpId(layerModeG2K(xcflayer.mode));
        kisLayer->setVisible(xcflayer.isVisible);
        kisLayer->disableAlphaChannel(xcflayer.mode != GIMP_NORMAL_MODE);

        layer.layer = kisLayer;
        layer.depth = xcflayer.pathLength;

        // Copy the data in the image
        initLayer(&xcflayer);

        int left = xcflayer.dim.c.l;
        int top = xcflayer.dim.c.t;

        if (!xcflayer.isGroup) {

            // Copy the data;
            for (unsigned int x = 0; x < xcflayer.dim.width; x += TILE_WIDTH) {
                for (unsigned int y = 0; y < xcflayer.dim.height; y += TILE_HEIGHT) {
                    rect want;
                    want.l = x + left;
                    want.t = y + top;
                    want.b = want.t + TILE_HEIGHT;
                    want.r = want.l + TILE_WIDTH;
                    Tile* tile = getMaskOrLayerTile(&xcflayer.dim, &xcflayer.pixels, want);
                    KisHLineIteratorSP it = kisLayer->paintDevice()->createHLineIteratorNG(x, y, TILE_WIDTH);
                    rgba* data = tile->pixels;
                    for (int v = 0; v < TILE_HEIGHT; ++v) {
                        if (isRgbA) {
                            // RGB image
                           do {
                                KoBgrTraits<quint8>::setRed(it->rawData(), GET_RED(*data));
                                KoBgrTraits<quint8>::setGreen(it->rawData(), GET_GREEN(*data));
                                KoBgrTraits<quint8>::setBlue(it->rawData(), GET_BLUE(*data));
                                KoBgrTraits<quint8>::setOpacity(it->rawData(), quint8(GET_ALPHA(*data)), 1);
                                ++data;
                            } while (it->nextPixel());
                        } else {
                            // Grayscale image
                            do {
                                it->rawData()[0] = GET_RED(*data);
                                it->rawData()[1] = GET_ALPHA(*data);
                                ++data;
                            } while (it->nextPixel());
                        }
                        it->nextRow();
                    }
                }
            }

            // Move the layer to its position
            kisLayer->paintDevice()->setX(left);
            kisLayer->paintDevice()->setY(top);
        }
        // Create the mask
        if (xcflayer.hasMask) {
            KisTransparencyMaskSP mask = new KisTransparencyMask();
            layer.mask = mask;

            mask->initSelection(kisLayer);
            for (unsigned int x = 0; x < xcflayer.dim.width; x += TILE_WIDTH) {
                for (unsigned int y = 0; y < xcflayer.dim.height; y += TILE_HEIGHT) {
                    rect want;
                    want.l = x + left;
                    want.t = y + top;
                    want.b = want.t + TILE_HEIGHT;
                    want.r = want.l + TILE_WIDTH;
                    Tile* tile = getMaskOrLayerTile(&xcflayer.dim, &xcflayer.mask, want);
                    KisHLineIteratorSP it = mask->paintDevice()->createHLineIteratorNG(x, y, TILE_WIDTH);
                    rgba* data = tile->pixels;
                    for (int v = 0; v < TILE_HEIGHT; ++v) {
                        do {
                            it->rawData()[0] = GET_ALPHA(*data);
                            ++data;
                        } while (it->nextPixel());
                        it->nextRow();
                    }

                }
            }
            mask->paintDevice()->setX(left);
            mask->paintDevice()->setY(top);
            image->addNode(mask, kisLayer);
        }

        dbgFile << xcflayer.pixels.tileptrs;
        layers.append(layer);
    }

    for (int i = 0; i <= maxDepth; ++i) {
        addLayers(layers, image, i);
    }

    doc->setCurrentImage(image);
    return KisImportExportFilter::OK;
}
void PSDLayerRecord::write(QIODevice* io,
                           KisPaintDeviceSP layerContentDevice,
                           KisNodeSP onlyTransparencyMask,
                           const QRect &maskRect,
                           psd_section_type sectionType,
                           const QDomDocument &stylesXmlDoc)
{
    dbgFile << "writing layer info record" << "at" << io->pos();

    m_layerContentDevice = layerContentDevice;
    m_onlyTransparencyMask = onlyTransparencyMask;
    m_onlyTransparencyMaskRect = maskRect;

    dbgFile << "saving layer record for " << layerName << "at pos" << io->pos();
    dbgFile << "\ttop" << top << "left" << left << "bottom" << bottom << "right" << right << "number of channels" << nChannels;
    Q_ASSERT(left <= right);
    Q_ASSERT(top <= bottom);
    Q_ASSERT(nChannels > 0);

    try {
        const QRect layerRect(left, top, right - left, bottom - top);
        KisAslWriterUtils::writeRect(layerRect, io);

        {
            quint16 realNumberOfChannels = nChannels + bool(m_onlyTransparencyMask);
            SAFE_WRITE_EX(io, realNumberOfChannels);
        }

        foreach(ChannelInfo *channel, channelInfoRecords) {
            SAFE_WRITE_EX(io, (quint16)channel->channelId);

            channel->channelInfoPosition = io->pos();

            // to be filled in when we know how big channel block is
            const quint32 fakeChannelSize = 0;
            SAFE_WRITE_EX(io, fakeChannelSize);
        }

        if (m_onlyTransparencyMask) {
            const quint16 userSuppliedMaskChannelId = -2;
            SAFE_WRITE_EX(io, userSuppliedMaskChannelId);

            m_transparencyMaskSizeOffset = io->pos();

            const quint32 fakeTransparencyMaskSize = 0;
            SAFE_WRITE_EX(io, fakeTransparencyMaskSize);
        }

        // blend mode
        dbgFile  << ppVar(blendModeKey) << ppVar(io->pos());

        KisAslWriterUtils::writeFixedString("8BIM", io);
        KisAslWriterUtils::writeFixedString(blendModeKey, io);

        SAFE_WRITE_EX(io, opacity);
        SAFE_WRITE_EX(io, clipping); // unused

        // visibility and protection
        quint8 flags = 0;
        if (transparencyProtected) flags |= 1;
        if (!visible) flags |= 2;
        if (irrelevant) {
            flags |= (1 << 3) | (1 << 4);
        }

        SAFE_WRITE_EX(io, flags);

        {
            quint8 padding = 0;
            SAFE_WRITE_EX(io, padding);
        }

        {
            // extra fields with their own length tag
            KisAslWriterUtils::OffsetStreamPusher<quint32> extraDataSizeTag(io);

            if (m_onlyTransparencyMask) {
                {
                    const quint32 layerMaskDataSize = 20; // support simple case only
                    SAFE_WRITE_EX(io, layerMaskDataSize);
                }

                KisAslWriterUtils::writeRect(m_onlyTransparencyMaskRect, io);

                {
                    KIS_ASSERT_RECOVER_NOOP(m_onlyTransparencyMask->paintDevice()->pixelSize() == 1);
                    const quint8 defaultPixel = *m_onlyTransparencyMask->paintDevice()->defaultPixel();
                    SAFE_WRITE_EX(io, defaultPixel);
                }

                {
                    const quint8 maskFlags = 0; // nothing serious
                    SAFE_WRITE_EX(io, maskFlags);

                    const quint16 padding = 0; // 2-byte padding
                    SAFE_WRITE_EX(io, padding);
                }
            } else {
                const quint32 nullLayerMaskDataSize = 0;
                SAFE_WRITE_EX(io, nullLayerMaskDataSize);
            }

            {
                // blending ranges are not implemented yet
                const quint32 nullBlendingRangesSize = 0;
                SAFE_WRITE_EX(io, nullBlendingRangesSize);
            }

            // layer name: Pascal string, padded to a multiple of 4 bytes.
            psdwrite_pascalstring(io, layerName, 4);


            PsdAdditionalLayerInfoBlock additionalInfoBlock(m_header);

            // write 'luni' data block
            additionalInfoBlock.writeLuniBlockEx(io, layerName);

            // write 'lsct' data block
            if (sectionType != psd_other) {
                additionalInfoBlock.writeLsctBlockEx(io, sectionType, isPassThrough, blendModeKey);
            }

            // write 'lfx2' data block
            if (!stylesXmlDoc.isNull()) {
                additionalInfoBlock.writeLfx2BlockEx(io, stylesXmlDoc);
            }
        }
    } catch (KisAslWriterUtils::ASLWriteException &e) {
Example #24
0
void KisToolFreehandHelper::paintBezierSegment(KisPaintInformation pi1, KisPaintInformation pi2,
        QPointF tangent1, QPointF tangent2)
{
    if (tangent1.isNull() || tangent2.isNull()) return;

    const qreal maxSanePoint = 1e6;

    QPointF controlTarget1;
    QPointF controlTarget2;

    // Shows the direction in which control points go
    QPointF controlDirection1 = pi1.pos() + tangent1;
    QPointF controlDirection2 = pi2.pos() - tangent2;

    // Lines in the direction of the control points
    QLineF line1(pi1.pos(), controlDirection1);
    QLineF line2(pi2.pos(), controlDirection2);

    // Lines to check whether the control points lay on the opposite
    // side of the line
    QLineF line3(controlDirection1, controlDirection2);
    QLineF line4(pi1.pos(), pi2.pos());

    QPointF intersection;
    if (line3.intersect(line4, &intersection) == QLineF::BoundedIntersection) {
        qreal controlLength = line4.length() / 2;

        line1.setLength(controlLength);
        line2.setLength(controlLength);

        controlTarget1 = line1.p2();
        controlTarget2 = line2.p2();
    } else {
        QLineF::IntersectType type = line1.intersect(line2, &intersection);

        if (type == QLineF::NoIntersection ||
                intersection.manhattanLength() > maxSanePoint) {

            intersection = 0.5 * (pi1.pos() + pi2.pos());
//            dbgKrita << "WARINING: there is no intersection point "
//                     << "in the basic smoothing algoriths";
        }

        controlTarget1 = intersection;
        controlTarget2 = intersection;
    }

    // shows how near to the controlTarget the value raises
    qreal coeff = 0.8;

    qreal velocity1 = QLineF(QPointF(), tangent1).length();
    qreal velocity2 = QLineF(QPointF(), tangent2).length();

    if (velocity1 == 0.0 || velocity2 == 0.0) {
        velocity1 = 1e-6;
        velocity2 = 1e-6;
        warnKrita << "WARNING: Basic Smoothing: Velocity is Zero! Please report a bug:" << ppVar(velocity1) << ppVar(velocity2);
    }

    qreal similarity = qMin(velocity1/velocity2, velocity2/velocity1);

    // the controls should not differ more than 50%
    similarity = qMax(similarity, qreal(0.5));

    // when the controls are symmetric, their size should be smaller
    // to avoid corner-like curves
    coeff *= 1 - qMax(qreal(0.0), similarity - qreal(0.8));

    Q_ASSERT(coeff > 0);


    QPointF control1;
    QPointF control2;

    if (velocity1 > velocity2) {
        control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1;
        coeff *= similarity;
        control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2;
    } else {
        control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2;
        coeff *= similarity;
        control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1;
    }

    paintBezierCurve(pi1,
                     control1,
                     control2,
                     pi2);
}
void KisOcioDisplayFilterTest::test()
{
    KisExposureGammaCorrectionInterface *egInterface =
        new KisDumbExposureGammaCorrectionInterface();

    OcioDisplayFilter filter(egInterface);

    QString configFile = TestUtil::fetchDataFileLazy("./psyfiTestingConfig-master/config.ocio");
    dbgKrita << ppVar(configFile);

    Q_ASSERT(QFile::exists(configFile));

    OCIO::ConstConfigRcPtr ocioConfig =
        OCIO::Config::CreateFromFile(configFile.toUtf8());

    filter.config = ocioConfig;
    filter.inputColorSpaceName = ocioConfig->getColorSpaceNameByIndex(0);
    filter.displayDevice = ocioConfig->getDisplay(1);
    filter.view = ocioConfig->getView(filter.displayDevice, 0);
    filter.gamma = 1.0;
    filter.exposure = 0.0;
    filter.swizzle = RGBA;

    filter.blackPoint = 0.0;
    filter.whitePoint = 1.0;

    filter.forceInternalColorManagement = false;
    filter.setLockCurrentColorVisualRepresentation(false);

    filter.updateProcessor();

    dbgKrita << ppVar(filter.inputColorSpaceName);
    dbgKrita << ppVar(filter.displayDevice);
    dbgKrita << ppVar(filter.view);
    dbgKrita << ppVar(filter.gamma);
    dbgKrita << ppVar(filter.exposure);

    const KoColorSpace *paintingCS =
                KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), 0);

    KisImageSP image = utils::createImage(0, QSize(100, 100));
    image->convertImageColorSpace(paintingCS, KoColorConversionTransformation::InternalRenderingIntent, KoColorConversionTransformation::InternalConversionFlags);
    image->waitForDone();

dbgKrita << ppVar(paintingCS) << ppVar(image->root()->firstChild()->colorSpace());

    KoCanvasResourceManager *resourceManager =
        utils::createResourceManager(image,
                                     image->root(), "");

    KisDisplayColorConverter converter(resourceManager, 0);

    dbgKrita << ppVar(image->root()->firstChild());

    QVariant v;
    v.setValue(KisNodeWSP(image->root()->firstChild()));
    resourceManager->setResource(KisCanvasResourceProvider::CurrentKritaNode, v);

    converter.setDisplayFilter(&filter);
    dbgKrita << ppVar(converter.paintingColorSpace());

    {
        QColor refColor(255, 128, 0);
        KoColor realColor = converter.approximateFromRenderedQColor(refColor);
        QColor roundTripColor = converter.toQColor(realColor);

        dbgKrita << ppVar(refColor);
        dbgKrita << ppVar(realColor.colorSpace()) << ppVar(KoColor::toQString(realColor));
        dbgKrita << ppVar(roundTripColor);
    }

    {
        KoColor realColor(Qt::red, paintingCS);
        QColor roundTripColor = converter.toQColor(realColor);

        dbgKrita << ppVar(realColor.colorSpace()) << ppVar(KoColor::toQString(realColor));
        dbgKrita << ppVar(roundTripColor);
    }

}
Example #26
0
bool KisXMPIO::loadFrom(KisMetaData::Store* store, QIODevice* ioDevice) const
{
    ioDevice->open(QIODevice::ReadOnly);
    dbgFile << "Load XMP Data";
    std::string xmpPacket_;
    QByteArray arr = ioDevice->readAll();
    xmpPacket_.assign(arr.data(), arr.length());
    dbgFile << xmpPacket_.length();
//     dbgFile << xmpPacket_.c_str();
    Exiv2::XmpData xmpData_;
    Exiv2::XmpParser::decode(xmpData_, xmpPacket_);
    QMap< const KisMetaData::Schema*, QMap<QString, QMap<QString, KisMetaData::Value> > > structures;
    QMap< const KisMetaData::Schema*, QMap<QString, QVector< QMap<QString, KisMetaData::Value> > > > arraysOfStructures;
    for (Exiv2::XmpData::iterator it = xmpData_.begin(); it != xmpData_.end(); ++it) {
        dbgFile << it->key().c_str();
        Exiv2::XmpKey key(it->key());
        dbgFile << key.groupName().c_str() << " " << key.tagName().c_str() << " " << key.ns().c_str();
        if ((key.groupName() == "exif" || key.groupName() == "tiff") && key.tagName() == "NativeDigest") {  // TODO: someone who has time to lose can look in adding support for NativeDigest, it's undocumented use by the XMP SDK to check if exif data has been changed while XMP hasn't been updated
            dbgFile << "dropped";
        } else {
            const KisMetaData::Schema* schema = KisMetaData::SchemaRegistry::instance()->schemaFromPrefix(key.groupName().c_str());
            if (!schema) {
                schema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(key.ns().c_str());
                if (!schema) {
                    schema = KisMetaData::SchemaRegistry::instance()->create(key.ns().c_str(), key.groupName().c_str());
                    Q_ASSERT(schema);
                }
            }
            const Exiv2::Value::AutoPtr value = it->getValue();
            // Decrypt key
            QString structName = "";
            QString tagName = key.tagName().c_str();
            int arrayIndex = -1;
            const KisMetaData::TypeInfo* typeInfo = 0;
            bool isStructureEntry = false;
            bool isStructureInArrayEntry = false;
            if (tagName.contains("/")) {
                QRegExp regexp("([A-Za-z]\\w+)/([A-Za-z]\\w+):([A-Za-z]\\w+)");
                if (regexp.indexIn(tagName) != -1) {
                    structName = regexp.capturedTexts()[1];
                    tagName =  regexp.capturedTexts()[3];
                    typeInfo = schema->propertyType(structName);
                    Q_ASSERT(typeInfo == schema->propertyType(structName));
                    if (typeInfo && typeInfo->propertyType() == KisMetaData::TypeInfo::StructureType) {
                        typeInfo = typeInfo->structureSchema()->propertyType(tagName);
                    }
                    isStructureEntry = true;
                } else {
                    QRegExp regexp2("([A-Za-z]\\w+)\\[(\\d+)\\]/([A-Za-z]\\w+):([A-Za-z]\\w+)");
                    if (regexp2.indexIn(tagName) != -1) {
                        dbgFile << ppVar(tagName);
                        structName = regexp2.capturedTexts()[1];
                        arrayIndex = regexp2.capturedTexts()[2].toInt() - 1;
                        tagName = regexp2.capturedTexts()[4];
                        dbgFile << ppVar(structName) << ppVar(regexp2.capturedTexts()[3]);
                        //Q_ASSERT(schema->propertyType(structName));
                        if (schema->propertyType(structName)) {
                            typeInfo = schema->propertyType(structName)->embeddedPropertyType();
                            Q_ASSERT(typeInfo);
                            if (typeInfo && typeInfo->propertyType() == KisMetaData::TypeInfo::StructureType) {
                                typeInfo = typeInfo->structureSchema()->propertyType(tagName);
                            }
                        }
                        isStructureInArrayEntry = true;
                    } else {
                        dbgFile << "Decoding structure name/entry failed: " << tagName;
                    }
                }
            } else {
                typeInfo = schema->propertyType(tagName);
            }
            KisMetaData::Value v;

            bool ignoreValue = false;
            // Compute the value
            if (value->typeId() == Exiv2::xmpBag
                    || value->typeId() == Exiv2::xmpSeq
                    || value->typeId() == Exiv2::xmpAlt) {
                const KisMetaData::TypeInfo* embeddedTypeInfo = 0;
                if (typeInfo) {
                    embeddedTypeInfo = typeInfo->embeddedPropertyType();
                }
                const KisMetaData::Parser* parser = 0;
                if (embeddedTypeInfo) {
                    parser = embeddedTypeInfo->parser();
                }
                const Exiv2::XmpArrayValue* xav = dynamic_cast<const Exiv2::XmpArrayValue*>(value.get());
                Q_ASSERT(xav);
                QList<KisMetaData::Value> array;
                for (std::vector< std::string >::const_iterator it = xav->value_.begin();
                        it != xav->value_.end(); ++it) {
                    QString value = it->c_str();
                    if (parser) {
                        array.push_back(parser->parse(value));
                    } else {
                        dbgImage << "No parser " << tagName;
                        array.push_back(KisMetaData::Value(value));
                    }
                }
                KisMetaData::Value::ValueType vt = KisMetaData::Value::Invalid;
                switch (xav->xmpArrayType()) {
                case Exiv2::XmpValue::xaNone:
                    warnKrita << "KisXMPIO: Unsupported array";
                    break;
                case Exiv2::XmpValue::xaAlt:
                    vt = KisMetaData::Value::AlternativeArray;
                    break;
                case Exiv2::XmpValue::xaBag:
                    vt = KisMetaData::Value::UnorderedArray;
                    break;
                case Exiv2::XmpValue::xaSeq:
                    vt = KisMetaData::Value::OrderedArray;
                    break;
                }
                v = KisMetaData::Value(array, vt);
            } else if (value->typeId() == Exiv2::langAlt) {
                const Exiv2::LangAltValue* xav = dynamic_cast<const Exiv2::LangAltValue*>(value.get());
                QList<KisMetaData::Value> alt;
                for (std::map< std::string, std::string>::const_iterator it = xav->value_.begin();
                        it != xav->value_.end(); ++it) {
                    KisMetaData::Value valt(it->second.c_str());
                    valt.addPropertyQualifier("xml:lang", KisMetaData::Value(it->first.c_str()));
                    alt.push_back(valt);
                }
                v = KisMetaData::Value(alt, KisMetaData::Value::LangArray);
            } else {
                QString valTxt = value->toString().c_str();
                if (typeInfo && typeInfo->parser()) {
                    v = typeInfo->parser()->parse(valTxt);
                } else {
                    dbgFile << "No parser " << tagName;
                    v = KisMetaData::Value(valTxt);
                }
                if (valTxt == "type=\"Struct\"") {
                    if (!typeInfo || typeInfo->propertyType() == KisMetaData::TypeInfo::StructureType) {
                        ignoreValue = true;
                    }
                }
            }

            // set the value
            dbgFile << ppVar(tagName);
            if (isStructureEntry) {
                structures[schema][structName][tagName] = v;
            } else if (isStructureInArrayEntry) {
                if (arraysOfStructures[schema][structName].size() <= arrayIndex) {
                    arraysOfStructures[schema][structName].resize(arrayIndex + 1);
                }
                arraysOfStructures[schema][structName][arrayIndex][tagName] = v;
            } else {
                if (!ignoreValue) {
                    store->addEntry(KisMetaData::Entry(schema, tagName, v));
                } else {
                    dbgFile << "Ignoring value for " << tagName << " " << v;
                }
            }
        }
    }

    for (QMap< const KisMetaData::Schema*, QMap<QString, QMap<QString, KisMetaData::Value>  > >::iterator it = structures.begin();
            it != structures.end(); ++it) {
        const KisMetaData::Schema* schema = it.key();
        for (QMap<QString, QMap<QString, KisMetaData::Value> >::iterator it2 = it.value().begin();
                it2 != it.value().end(); ++it2) {
            store->addEntry(KisMetaData::Entry(schema, it2.key(), KisMetaData::Value(it2.value())));
        }
    }
    for (QMap< const KisMetaData::Schema*, QMap<QString, QVector< QMap<QString, KisMetaData::Value> > > >::iterator it = arraysOfStructures.begin(); it != arraysOfStructures.end(); ++it) {
        const KisMetaData::Schema* schema = it.key();
        for (QMap<QString, QVector<QMap<QString, KisMetaData::Value> > >::iterator it2 = it.value().begin();
                it2 != it.value().end(); ++it2) {
            KisMetaData::Value::ValueType type = KisMetaData::Value::OrderedArray;
            QString entryName = it2.key();
            if (schema->propertyType(entryName)) {
                switch (schema->propertyType(entryName)->propertyType()) {
                case KisMetaData::TypeInfo::OrderedArrayType:
                    type = KisMetaData::Value::OrderedArray;
                    break;
                case KisMetaData::TypeInfo::UnorderedArrayType:
                    type = KisMetaData::Value::OrderedArray;
                    break;
                case KisMetaData::TypeInfo::AlternativeArrayType:
                    type = KisMetaData::Value::AlternativeArray;
                    break;
                default:
                    type = KisMetaData::Value::Invalid;
                    break;
                }
            } else if (store->containsEntry(schema, entryName)) {
                KisMetaData::Value value = store->getEntry(schema, entryName).value();
                if (value.isArray()) {
                    type = value.type();
                }
            }
            store->removeEntry(schema, entryName);
            if (type != KisMetaData::Value::Invalid) {
                QList< KisMetaData::Value > valueList;
                for (int i = 0; i < it2.value().size(); ++i) {
                    valueList.append(it2.value()[i]);
                }
                store->addEntry(KisMetaData::Entry(schema, entryName, KisMetaData::Value(valueList, type)));
            }
        }
    }

    return true;
}