File indexing completed on 2025-01-26 04:10:29

0001 /*
0002  * SPDX-FileCopyrightText: 2013 Dmitry Kazakov <dimula73@gmail.com>
0003  * SPDX-FileCopyrightText: 2013 Lukáš Tvrdý <lukast.dev@gmail.com>
0004  * SPDX-FileCopyrightText: 2022 L. E. Segovia <amy@amyspark.me>
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include <QRegularExpression>
0010 
0011 #include <KoCompositeOpRegistry.h>
0012 #include <commands/KisNodeRenameCommand.h>
0013 #include <commands/kis_node_compositeop_command.h>
0014 #include <commands/kis_node_opacity_command.h>
0015 #include <commands_new/kis_node_move_command2.h>
0016 #include <kis_command_utils.h>
0017 #include <kis_layer_utils.h>
0018 #include <kis_node.h>
0019 #include <kis_paint_layer.h>
0020 #include <kis_painter.h>
0021 #include <kis_selection.h>
0022 #include <kis_types.h>
0023 
0024 #include "gmic.h"
0025 #include "kis_qmic_import_tools.h"
0026 #include "kis_qmic_interface.h"
0027 #include "kis_qmic_simple_convertor.h"
0028 
0029 namespace KisQmicImportTools
0030 {
0031 [[nodiscard]] KUndo2Command *
0032 applyLayerNameChanges(const KisQMicImage &srcGmicImage,
0033                       KisNode *node,
0034                       KisSelectionSP selection)
0035 {
0036     dbgPlugins << "KisQmicImportTools::applyLayerNameChanges";
0037 
0038     auto *cmd = new KisCommandUtils::CompositeCommand();
0039 
0040     dbgPlugins << "Layer name: " << srcGmicImage.m_layerName;
0041 
0042     {
0043         const QRegExp modeRe(R"(mode\(\s*([^)]*)\s*\))");
0044         if (modeRe.indexIn(srcGmicImage.m_layerName) != -1) {
0045             QString modeStr = modeRe.cap(1).trimmed();
0046             auto translatedMode =
0047                 KisQmicSimpleConvertor::stringToBlendingMode(modeStr);
0048             dbgPlugins << "Detected mode: " << modeStr << " => "
0049                        << translatedMode;
0050             if (!translatedMode.isNull()) {
0051                 cmd->addCommand(
0052                     new KisNodeCompositeOpCommand(node, translatedMode));
0053             }
0054         }
0055     }
0056 
0057     {
0058         const QRegExp opacityRe(R"(opacity\(\s*([^)]*)\s*\))");
0059 
0060         if (opacityRe.indexIn(srcGmicImage.m_layerName) != -1) {
0061             const auto opacity = opacityRe.cap(1).toFloat();
0062             dbgPlugins << "Detected opacity: " << opacity
0063                        << std::lround(opacity / 100.f * 255.f);
0064             cmd->addCommand(
0065                 new KisNodeOpacityCommand(node,
0066                                           static_cast<quint8>(std::lround(
0067                                               float(opacity * 255) / 100.f))));
0068         }
0069     }
0070 
0071     {
0072         const QRegExp nameRe(R"(name\(\s*([^)]*)\s*\))");
0073 
0074         if (nameRe.indexIn(srcGmicImage.m_layerName) != -1) {
0075             const auto name = nameRe.cap(1);
0076             dbgPlugins << "Detected layer name: " << name;
0077             cmd->addCommand(new KisNodeRenameCommand(node, node->name(), name));
0078             // apply command
0079         }
0080     }
0081 
0082     if (!selection) {
0083         // Some GMic filters encode layer position into the layer name.
0084         // E.g. from extract foreground: "name([unnamed]
0085         // [foreground]),pos(55,35)"
0086         const QRegularExpression positionPattern(
0087             R"(pos\(\s*(-?\d*)[^)](-?\d*)\s*\))");
0088         const QRegularExpressionMatch match =
0089             positionPattern.match(srcGmicImage.m_layerName);
0090         if (match.hasMatch()) {
0091             const auto x = match.captured(1).toInt();
0092             const auto y = match.captured(2).toInt();
0093             const QPoint oldPos(node->x(), node->y());
0094             const QPoint newPos(x, y);
0095             dbgPlugins << "Detected layer position: " << oldPos << newPos
0096                        << node->paintDevice()->exactBounds();
0097             cmd->addCommand(new KisNodeMoveCommand2(node, oldPos, newPos));
0098         }
0099     }
0100 
0101     return cmd;
0102 }
0103 
0104 void gmicImageToPaintDevice(const KisQMicImage &srcGmicImage,
0105                             KisPaintDeviceSP dst,
0106                             KisSelectionSP selection,
0107                             const QRect &dstRect)
0108 {
0109     dbgPlugins << "KisQmicImportTools::gmicImageToPaintDevice()" << dstRect;
0110 
0111     if (selection) {
0112         KisPaintDeviceSP src = new KisPaintDevice(dst->colorSpace());
0113         KisQmicSimpleConvertor::convertFromGmicFast(srcGmicImage, src, 255.0f);
0114         KisPainter painter(dst, selection);
0115         painter.setCompositeOpId(COMPOSITE_COPY);
0116         painter.bitBlt(dstRect.topLeft(),
0117                        src,
0118                        QRect(QPoint(0, 0), dstRect.size()));
0119     } else {
0120         KisQmicSimpleConvertor::convertFromGmicFast(srcGmicImage, dst, 255.0f);
0121     }
0122 }
0123 
0124 KisNodeListSP
0125 inputNodes(KisImageSP image, InputLayerMode inputMode, KisNodeSP currentNode)
0126 {
0127     /*
0128         ACTIVE_LAYER,
0129         ALL_LAYERS,
0130         ACTIVE_LAYER_BELOW_LAYER,
0131         ACTIVE_LAYER_ABOVE_LAYER,
0132         ALL_VISIBLE_LAYERS,
0133         ALL_INVISIBLE_LAYERS,
0134         ALL_VISIBLE_LAYERS_DECR,
0135         ALL_INVISIBLE_DECR,
0136         ALL_DECR
0137     */
0138     const auto isAvailable = [](KisNodeSP node) -> bool {
0139         auto *paintLayer = dynamic_cast<KisPaintLayer *>(node.data());
0140         return paintLayer && paintLayer->visible(false);
0141     };
0142 
0143     KisNodeListSP result(new QList<KisNodeSP>());
0144     switch (inputMode) {
0145     case InputLayerMode::NoInput: {
0146         break;
0147     }
0148     case InputLayerMode::Active: {
0149         if (isAvailable(currentNode)) {
0150             result->prepend(currentNode);
0151         }
0152         break; // drop down in case of one more layer modes
0153     }
0154     case InputLayerMode::All: {
0155         result = [&]() {
0156             KisNodeListSP r(new QList<KisNodeSP>());
0157             KisLayerUtils::recursiveApplyNodes(
0158                 image->root(),
0159                 [&](KisNodeSP item) {
0160                     auto *paintLayer =
0161                         dynamic_cast<KisPaintLayer *>(item.data());
0162                     if (paintLayer) {
0163                         r->prepend(item);
0164                     }
0165                 });
0166             return r;
0167         }();
0168         break;
0169     }
0170     case InputLayerMode::ActiveAndBelow: {
0171         if (isAvailable(currentNode)) {
0172             result->prepend(currentNode);
0173             if (isAvailable(currentNode->prevSibling())) {
0174                 result->prepend(currentNode->prevSibling());
0175             }
0176         }
0177         break;
0178     }
0179     case InputLayerMode::ActiveAndAbove: {
0180         if (isAvailable(currentNode)) {
0181             result->prepend(currentNode);
0182             if (isAvailable(currentNode->nextSibling())) {
0183                 result->prepend(currentNode->nextSibling());
0184             }
0185         }
0186         break;
0187     }
0188     case InputLayerMode::AllVisible:
0189     case InputLayerMode::AllInvisible: {
0190         const bool visibility = (inputMode == InputLayerMode::AllInvisible);
0191 
0192         result = [&]() {
0193             KisNodeListSP r(new QList<KisNodeSP>());
0194             KisLayerUtils::recursiveApplyNodes(
0195                 image->root(),
0196                 [&](KisNodeSP item) {
0197                     auto *paintLayer =
0198                         dynamic_cast<KisPaintLayer *>(item.data());
0199                     if (paintLayer
0200                         && paintLayer->visible(false) == visibility) {
0201                         r->prepend(item);
0202                     }
0203                 });
0204             return r;
0205         }();
0206         break;
0207     }
0208     case InputLayerMode::Unspecified:
0209     default: {
0210         qWarning()
0211             << "Inputmode" << static_cast<int>(inputMode)
0212             << "must be specified by GMic or is not implemented in Krita";
0213         break;
0214     }
0215     }
0216     return result;
0217 }
0218 } // namespace KisQmicImportTools